When your server sets a cookie — for a session, an authentication token, a preference — it can attach flags that control how browsers handle it. Three flags matter most for security: HttpOnly, Secure and SameSite. Missing any of them on a sensitive cookie is a common vulnerability that leaves users open to session theft, eavesdropping and cross-site request forgery.
HttpOnly — keep cookies out of JavaScript
Without HttpOnly, any JavaScript running on your page can read the cookie with
document.cookie. That includes injected scripts from a
cross-site scripting (XSS) attack. An attacker who can
run script on your page can silently steal every session token in the browser — without the user knowing.
Adding HttpOnly blocks JavaScript from accessing the cookie entirely. The browser still sends
it with requests automatically, but no script — yours or an attacker's — can read it. This is the most important
flag for any authentication cookie.
Set-Cookie: session=abc123; HttpOnly
Secure — cookies over HTTPS only
The Secure flag tells the browser to only send the cookie over HTTPS connections, never over
plain HTTP. Without it, if a user visits even one page of your site over HTTP (a redirect, a mixed-content
resource, a network that strips HTTPS), the cookie travels in plain text — visible to anyone watching the
network. This is especially dangerous on public Wi-Fi.
Combine Secure with HSTS for belt-and-suspenders protection:
HSTS ensures HTTPS is always used; Secure ensures the cookie is never sent even if somehow HTTP
is reached.
Set-Cookie: session=abc123; Secure; HttpOnly
SameSite — block cross-site request forgery
The SameSite attribute controls whether the browser sends a cookie on requests that originate
from a different site. This directly defends against CSRF attacks, where a
malicious page tricks your browser into making an authenticated request to your site.
There are three values:
- SameSite=Strict — the cookie is never sent on cross-site requests at all, including when clicking a link from another site. Maximum protection, but can break flows where users arrive via a link (e.g. from an email).
- SameSite=Lax — the cookie is sent on same-site requests and on top-level navigations (like clicking a link), but not on sub-requests (images, iframes, AJAX). This is the modern browser default and a good balance for most sites.
- SameSite=None — the cookie is sent on all requests, including cross-site. Required for
things like embedded widgets or cross-origin APIs. Must be combined with
Secure.
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax
The complete secure session cookie
A properly hardened authentication cookie uses all three flags:
Set-Cookie: session=abc123;
Path=/;
HttpOnly;
Secure;
SameSite=Lax;
Max-Age=86400
What each part does:
| Flag | Protects against |
|---|---|
HttpOnly | XSS session theft via document.cookie |
Secure | Network sniffing on HTTP connections |
SameSite=Lax | CSRF attacks from third-party sites |
Max-Age | Limits session lifetime |
Common mistakes
- HttpOnly on non-sensitive cookies only. Every cookie that controls access, identity or privilege should be HttpOnly — not just the main session cookie.
- SameSite=None without Secure. Modern browsers reject cookies with
SameSite=NoneifSecureis absent. - Assuming the framework sets these by default. Many frameworks don't. Check your session library's documentation and test what headers your site actually sends.
- Missing flags on JWT tokens stored in cookies. A JWT in a cookie is still a cookie — it needs the same flags as a session ID.
How to check your cookies
In Chrome DevTools: Application → Cookies → select your domain. The table shows every cookie with its flags. Any sensitive cookie missing HttpOnly, Secure or a SameSite value is a gap worth fixing.
A passive scan can also audit your server's Set-Cookie response headers alongside your other
security controls — flag any authentication cookies missing key protections — and give you a single grade.
Run a free scan to see where your domain stands.
FAQ
Can I set these flags on existing cookies without breaking anything?
Yes, in most cases. HttpOnly and Secure are transparent to the browser's request
behaviour — they only restrict access. If your JavaScript currently reads the cookie via
document.cookie, you'll need to adjust that code, but that's usually a sign the token should be
stored differently anyway.
Does SameSite=Strict break login flows from email links?
It can: if a user clicks "log in" from an email link, the browser treats that as a cross-site navigation and
won't send a Strict cookie. SameSite=Lax handles top-level navigations (including email links)
without this problem and is a safer default for most applications.
What about cookies set by third-party scripts (analytics, ads)?
Third-party scripts set their own cookies — you control yours, not theirs. Focus on the cookies your server sends for authentication, sessions and preferences.