Cookie Security: HttpOnly, Secure, and SameSite
Part of our guide to HTTP Security Headers Explained: The Complete Guide.
Three cookie attributes do most of the heavy lifting for session security: HttpOnly hides a cookie from JavaScript, Secure restricts it to HTTPS, and SameSite governs whether it rides along on cross-site requests. Set all three on a session cookie and you have shut down the common paths to cookie theft and forgery. Leave them off and a single scripting bug can hand an attacker a logged-in session.
Cookies travel as response headers, so they belong in the same conversation as the rest of your HTTP security headers explained. A server sets one with a Set-Cookie header on the response; the browser stores it and returns it on later requests to the same site. The attributes that follow the value control how the browser treats it.
How a cookie gets set
When a server wants the browser to remember something, it sends a header like this:
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
The first part, session=abc123, is the name and value. Everything after the semicolons is an attribute. The browser sends the cookie back on matching requests until it expires or is cleared. The behaviour of this header is defined in RFC 6265, HTTP State Management Mechanism, which remains the reference for how cookies and their attributes work.
The three security attributes
HttpOnly
HttpOnly removes the cookie from JavaScript's reach. Code running on the page cannot read it through document.cookie, which is the key defense against cross-site scripting attacks that try to exfiltrate session tokens. If a malicious script slips onto your page, it still cannot scoop up an HttpOnly session cookie and ship it to an attacker's server. Set this on every cookie that the browser's scripts have no legitimate reason to read, which is almost all session cookies.
Secure
Secure tells the browser to attach the cookie only to requests sent over HTTPS. Without it, a cookie can leak over a plain HTTP connection, where anyone on the network path can read it. On a site that already redirects everything to HTTPS this might feel redundant, but it closes the gap during that first insecure request and protects against accidental mixed-content links.
SameSite
SameSite decides whether the cookie travels on requests that originate from another site, which is the lever that mitigates cross-site request forgery. It takes three values:
- Strict: the cookie is sent only when the request originates from the same site. Strongest, but a link from an external page will not carry the session, so the user may appear logged out when arriving from elsewhere.
- Lax: the cookie is withheld on cross-site subrequests (images, frames, background fetches) but sent on top-level navigations like clicking a link. A sensible default for most session cookies.
- None: the cookie is sent on all cross-site requests. Required for legitimate cross-site use such as embedded widgets, but it must be paired with Secure or browsers will reject it.
Session versus persistent cookies
A cookie's lifetime is separate from its security attributes. Omit both Expires and Max-Age and you get a session cookie, which the browser discards when it closes. Add Max-Age=3600 or an Expires date and it becomes persistent, surviving restarts until that moment. Session cookies are the safer default for authentication: the shorter a credential lives, the smaller the window for misuse. Apply HttpOnly, Secure, and SameSite regardless of which lifetime you choose.
Quick reference
The OWASP Secure Headers project recommends setting all three on session cookies as a baseline.
| Attribute | Protects against | Notes |
|---|---|---|
| HttpOnly | XSS-based cookie theft | Blocks document.cookie access |
| Secure | Network interception over HTTP | Cookie sent only over HTTPS |
| SameSite=Strict | CSRF | Same-site requests only |
| SameSite=Lax | CSRF | Allows top-level cross-site navigation |
| SameSite=None | (none on its own) | Cross-site allowed; requires Secure |
Put together, a hardened session cookie looks like the Set-Cookie example above: HttpOnly so scripts cannot touch it, Secure so it never crosses plain HTTP, and SameSite=Lax (or Strict for sensitive flows) so forged cross-site requests cannot use it.
Not sure how your own site sets its cookies, or whether the rest of your headers measure up? Scan any site's headers free at domainintel.app for a graded report and a list of fixes.
Frequently asked questions
What does the HttpOnly cookie flag do?
HttpOnly blocks JavaScript from reading the cookie via document.cookie, which stops an attacker from stealing a session cookie through a cross-site scripting (XSS) flaw.
What does SameSite do?
SameSite controls whether a cookie is sent on cross-site requests, which mitigates cross-site request forgery (CSRF). It takes the values Strict, Lax, and None; None requires the Secure attribute.
What does the Secure flag do?
Secure tells the browser to send the cookie only over HTTPS connections, so it is never transmitted in plaintext where it could be intercepted.