SameSite Cookies Explained: Strict, Lax and None
The SameSite attribute on a cookie decides whether the browser sends it on requests that come from another site. It's the main reason cross-site request forgery (CSRF) is much harder than it used to be — and the reason some logins, payment redirects and embedded widgets mysteriously "forget" who you are.
Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax
"Site" is not "origin"
SameSite works on sites, which are broader than origins. A site is the scheme plus the registrable domain: https://app.example.com and https://api.example.com are different origins but the same site (https://example.com). A request between them is same-site, so SameSite doesn't restrict it.
https://example.com and https://example.org are different sites. So are http://example.com and https://example.com — browsers now treat the scheme as part of the site.
The three values
| Value | Sent on cross-site requests? |
|---|---|
Strict |
Never. Not even when the user clicks a link to your site from elsewhere. |
Lax |
Only on top-level navigations with a safe method — following a link, typing the URL. Not on cross-site POSTs, iframes, images or fetches. |
None |
Always. Requires Secure. |
What that means for common requests arriving from another site:
| Request from another site | Strict | Lax | None |
|---|---|---|---|
| User clicks a link to your page | ✗ | ✓ | ✓ |
| Form POST to your site | ✗ | ✗ | ✓ |
Your page in an <iframe> |
✗ | ✗ | ✓ |
<img>, <script> or fetch() to your site |
✗ | ✗ | ✓ |
The default is Lax — in some browsers
Chrome and Edge treat a cookie without a SameSite attribute as Lax. Firefox and Safari don't apply that default, so the same cookie behaves differently across browsers. Always set SameSite explicitly.
Chrome also has a small exception for cookies with no attribute: for the first two minutes after they're set, they're still sent on cross-site top-level POSTs, so older single sign-on flows keep working. Cookies that say SameSite=Lax explicitly don't get that grace period.
Which one to use
- Session cookies for a normal website:
Lax. Users who follow a link to you arrive logged in, and cross-site POSTs — the classic CSRF vector — arrive without the cookie. - Sensitive actions (banking, admin):
Strict, often on a second cookie. The trade-off: users clicking a link from email or chat land on your site looking logged out until the next navigation. - Cookies that must work cross-site — embedded widgets, third-party integrations:
None; Secure. Expect browsers to restrict these further: Safari blocks third-party cookies by default and Firefox partitions them per site.
Why logins break: the cross-site POST
Some sign-in providers and payment pages send the user back to your site with a form POST (OAuth's response_mode=form_post, Sign in with Apple, many 3-D Secure flows). That's a cross-site POST, so a Lax or Strict cookie holding the login state isn't sent, and your callback can't find the session.
Fixes, in order of preference:
- Use a redirect (GET) callback if the provider supports it.
- Mark only the short-lived state cookie used by the callback as
SameSite=None; Secure, and keep the session cookieLax.
SameSite and CSRF
Lax stops most CSRF, because a forged form on another site can't make the browser send your cookie with a POST. It isn't complete protection:
- Same-site isn't same-origin. A compromised or user-controlled subdomain (
evil.example.com) is same-site and can still send cookie-bearing requests. - GET requests that change state are still exposed to top-level navigations. Don't change data on GET.
- Older browsers ignore the attribute.
Keep CSRF tokens or check the Origin header on state-changing requests, and treat SameSite as a strong second layer.
Debugging
In Chrome DevTools, open Application → Cookies to see each cookie's SameSite value, and the Network tab's Cookies view for a request: blocked cookies are shown with the reason, such as "this cookie was blocked because it had the SameSite=Lax attribute and the request was made from a different site".
For every cookie attribute — HttpOnly, Secure, Max-Age, Domain, Path — see the Set-Cookie reference; for sending cookies on cross-origin fetches, see Access-Control-Allow-Credentials.