A session cookie is a credential. It is the thing that says "this browser is logged in as this person", and anyone who obtains it is that person until it expires. Three attributes decide how well it is protected, and all three are one line of configuration.
Check what a site is setting with the Cookie Security Checker.
Secure
Set-Cookie: session=abc123; Secure
The cookie is only ever sent over HTTPS. Without it, a single plain-HTTP request — a mistyped link, an old bookmark, an image URL somebody hardcoded — sends the session cookie in clear text across the network.
People assume this does not matter on an HTTPS-only site. It does. Your server may redirect HTTP to HTTPS, but the cookie was already transmitted in that first plain request before the redirect happened. The redirect is too late.
There is no reason to omit this on any production site.
HttpOnly
Set-Cookie: session=abc123; Secure; HttpOnly
JavaScript cannot read the cookie — document.cookie simply does not show it. The browser still sends it with every request, so nothing breaks.
This is your damage limitation for cross-site scripting. If an attacker gets script running on your page and your session cookie is HttpOnly, they cannot simply read it and post it to their server. They can still act as the user within that page, which is bad, but they cannot walk away with a credential that works from anywhere.
The only reason to leave it off is if your own JavaScript needs to read that cookie. For a session token, it should not.
SameSite
This one is the CSRF control, and it has three values that behave quite differently.
SameSite=Strict — the cookie is never sent on any cross-site request, including ordinary link clicks. Maximum protection, with a real usability cost: a user following a link to your site from an email arrives logged out, because the cookie was withheld. They log in, and it works from then on. Confusing for them the first time.
SameSite=Lax — sent on top-level navigations using safe methods (clicking a link), withheld on cross-site POSTs, iframes and background requests. This blocks the classic CSRF pattern while keeping links working. It is the right default for most sessions, and it is what browsers now assume when the attribute is missing.
SameSite=None — always sent. Required for genuine cross-site use such as an embedded widget or a third-party SSO flow. Browsers require it to be paired with Secure, and setting None without Secure means the cookie is rejected entirely — which produces a baffling "my login just stopped working" bug.
The attributes people forget
Domain — leave it off unless you need the cookie shared across subdomains. Setting Domain=example.com sends the cookie to every subdomain, including any you do not control tightly. A forgotten staging or marketing subdomain becomes a route to your session cookies.
Path — mild scoping. Not a security boundary; do not rely on it.
Max-Age / Expires — a session cookie with no expiry dies when the browser closes, which is usually what you want for authentication. A "remember me" cookie that lasts a year is a decision worth making deliberately rather than by default.
A correct session cookie
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/
That is it. Three attributes and a path. If your session cookie looks like that, this whole class of problem is handled.
For a cookie that must work inside a third-party iframe:
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=None; Path=/
And in that case your CSRF protection has to come from tokens instead, because SameSite is no longer doing that job.
What flags do not fix
These attributes protect the cookie in transit and at rest in the browser. They say nothing about what is inside it.
A predictable session identifier is still guessable with every flag set. A session that never expires server-side is still valid forever. And if your application does not invalidate sessions on logout or password change, a stolen cookie keeps working regardless of how carefully it was transmitted.
Checking a site
- Run it through the Cookie Security Checker, or open DevTools and read the Application tab.
- Find the session cookie — usually the one with a random-looking value.
- Confirm Secure and HttpOnly are both set.
- Confirm SameSite is
LaxorStrict, and that anyNoneis paired withSecure. - Check whether
Domainis set more broadly than it needs to be.
Common questions
Does HttpOnly stop XSS?
No. It limits the damage. An attacker with script execution can still act as the user in that page; they just cannot steal the cookie for use elsewhere.
Strict or Lax?
Lax for almost everything. Strict logs users out when they arrive from an external link, which is usually not worth the trade.
Why did my cookie stop working after I set SameSite=None?
Because None requires Secure. Without it the browser rejects the cookie outright.
Do I need Secure if my site is HTTPS-only?
Yes. A single plain-HTTP request transmits the cookie before your redirect can intervene.
Where to go next
- Check a site with the Cookie Security Checker.
- Cookie flags sit alongside security headers — both are server configuration, both take an afternoon.
- If your session is a token rather than a cookie, the JWT article covers what to check instead.
- Session handling is covered in the free BitCops courses, and the community is there if a flag is behaving oddly.

Comments
No comments yet. Be the first to add one.
Leave a comment
Your email is required so we can reply, and is never published or shared. Comments are reviewed before they appear.