Articles

Cookie Attributes Explained: HttpOnly, Secure, SameSite

HttpOnly, Secure, and SameSite are cookie attributes that block script access, force HTTPS, and limit cross-site sending. Here's what each one actually stops.

Chisato Chisato · · 4 min read
A padlock resting on a keyboard

Cookie attributes are flags set alongside a cookie’s name and value that tell the browser how to handle it: whether JavaScript can read it, whether it should only travel over HTTPS, and whether it should be sent on requests originating from other sites. HttpOnly, Secure, and SameSite are the three that matter most for security, and each one closes off a different attack, not the same one three times.

Setting them

A cookie with all three attributes looks like this in a Set-Cookie response header:

Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/

There’s no default that makes all three safe automatically — omitting any one of them leaves that particular door open, regardless of what the others are set to.

HttpOnly: blocking JavaScript access

HttpOnly tells the browser not to expose the cookie to document.cookie or any other script-accessible API. The cookie still gets sent with HTTP requests as normal; it’s just invisible to client-side JavaScript.

This is aimed squarely at cross-site scripting: if an attacker manages to inject a script into your page, HttpOnly stops that script from reading the session cookie and exfiltrating it to another server. It doesn’t stop XSS from happening — it limits what a successful XSS attack can steal. Session and authentication cookies should essentially always be HttpOnly; there’s rarely a legitimate reason for client-side code to read a session identifier directly.

Secure: requiring HTTPS

Secure tells the browser to only send the cookie over an HTTPS connection, never plain HTTP. Without it, a cookie set on a secure page could still leak over an unencrypted connection — say, if a user follows an http:// link to the same domain, or a mixed-content resource on the page happens to load over HTTP — exposing it to anyone positioned to intercept unencrypted traffic on the network. Combined with HSTS, which tells the browser to never attempt an HTTP connection to your domain at all, Secure closes a gap that HSTS alone doesn’t fully cover for cookies set before HSTS takes effect. There’s essentially no modern reason to omit Secure on any cookie that matters, given that plain HTTP is now the exception rather than the norm — see HTTPS explained for why.

SameSite: limiting cross-site sending

SameSite is the newest and most nuanced of the three. It controls whether a cookie is sent on requests that originate from a different site than the one the cookie belongs to — the classic scenario being a form or image tag on evil.example that triggers a request to your-bank.example, carrying your session cookie along with it. That’s the mechanism behind CSRF attacks: the cookie proves you’re logged in, and the browser sends it regardless of which site initiated the request, unless SameSite says otherwise.

Three values, each a different tradeoff:

  • Strict — the cookie is never sent on cross-site requests, including a user clicking a link from an external site to yours. Most secure, but can break normal navigation-based flows (e.g., a user follows a link from an email into an app and appears logged out).
  • Lax — the cookie is sent on top-level navigations (clicking a link) but not on cross-site subresource requests like images, iframes, or background fetches, and not on cross-site form submissions. This is the modern browser default when SameSite is unset, and it blocks the most common CSRF pattern while keeping normal navigation working.
  • None — the cookie is sent on all cross-site requests, same as historical behavior with no SameSite support at all. Browsers require Secure alongside SameSite=None, refusing to honor the combination without it. This is only appropriate for cookies that genuinely need cross-site use, such as third-party embedded widgets.
AttributeBlocksDoesn’t block
HttpOnlyJavaScript reading the cookieThe cookie being sent over HTTP, or cross-site
SecureSending the cookie over plain HTTPJavaScript access, or cross-site sending
SameSite=Lax/StrictSending the cookie on cross-site requestsJavaScript access, or interception over HTTP

They stack — a session cookie should generally carry all three (HttpOnly; Secure; SameSite=Lax at minimum) because each one addresses a distinct threat model.

Where this fits with modern auth

If your app issues a JWT for API authentication rather than a traditional server session, the same attributes still apply if you store that token in a cookie rather than in client-side storage — and storing it in an HttpOnly cookie is generally safer than localStorage, which is fully readable by any script running on the page. Frameworks handling OAuth or OIDC flows, and increasingly passkey-based logins, still typically fall back to a cookie to maintain the resulting session, so these attributes remain relevant even as the initial login mechanism changes.

The takeaway

HttpOnly, Secure, and SameSite each stop a different attack: script-based theft, plaintext interception, and cross-site request forgery, respectively. None of them substitutes for the others, and a cookie missing any one of them is missing that specific protection regardless of what’s set elsewhere. For session and authentication cookies, set all three — HttpOnly, Secure, and at minimum SameSite=Lax — and reserve SameSite=None for the narrow case of cookies that genuinely need cross-site delivery.

Chisato Chisato · · 5 min read

What Is Session Fixation?

Session fixation tricks a victim into using an attacker-known session ID, so logging in hands the attacker an authenticated session too.

#Security #Authentication #Web Development
Chisato Chisato · · 4 min read

The OAuth PKCE Flow Explained

PKCE hardens the OAuth authorization code flow against interception, and is now recommended for every client type, not just mobile and single-page apps.

#Security #Authentication #Web Development