Website problems
Cookies without SameSite
The `SameSite` attribute controls whether a cookie is sent on requests originating from another site. Without it, a form submitted from an attacker's page, or an image tag pointing at your endpoint, arrives carrying the visitor's session — which is the mechanism behind cross-site request forgery. Browsers now default unspecified cookies to `Lax`, which removes the worst of the exposure, but relying on a default rather than declaring the value leaves the behaviour to the browser rather than to you.
What each value does
`Strict`: the cookie is never sent on a cross-site request. The strongest setting, and it means a visitor following a link from another site arrives logged out — which is correct for a banking action and wrong for a content site.
`Lax`: the cookie is sent on top-level navigations using safe methods, typically clicking a link, and not on cross-site POSTs, iframes, images or fetches. This is the balance most sessions want.
`None`: the cookie is sent on every cross-site request. Required for genuine cross-site use — an embedded widget, a single sign-on flow — and it must be paired with `Secure` or browsers reject it entirely.
Unspecified: browsers apply `Lax` as the default. The behaviour is close to declaring `Lax`, and the difference is that a default can change and a declaration cannot.
The `Lax` default also comes with a two-minute exception in some browsers for recently set cookies on cross-site POSTs, which is exactly the kind of nuance that makes relying on defaults unwise.
Why None without Secure fails completely
A cookie declaring `SameSite=None` without `Secure` is rejected by the browser. Not downgraded — rejected, so the cookie is never stored.
The symptom is a feature that silently stops working: an embedded widget that cannot keep state, a sign-on flow that loops back to the login page, a payment iframe that loses its context.
Because the cookie is never set, nothing appears in the request to debug. The failure looks like a session problem rather than a cookie problem.
The rule is simple and absolute: `SameSite=None` always requires `Secure`, and therefore always requires HTTPS.
Choosing a value
Session cookies for an ordinary site: `Lax`, declared explicitly. A visitor arriving from a search result stays logged in; a cross-site POST does not carry the session.
Cookies protecting a destructive or financial action: `Strict`, with the understanding that inbound links will arrive unauthenticated.
Cookies for content genuinely embedded on other sites: `None` with `Secure`, and a deliberate decision that the cross-site exposure is acceptable and mitigated elsewhere.
Cookies that do not need to exist: remove them. The cheapest way to get the attribute right is to have fewer cookies.
And `SameSite` is a defence in depth rather than a complete one. It does not replace CSRF tokens for state-changing requests, and treating it as a replacement is the common over-reading.
How to check it yourself
The browser's Application panel shows `SameSite` per cookie, including where the browser applied a default rather than reading a declaration.
`curl -I https://example.com | grep -i set-cookie` shows what the server declares, which is the thing you control.
Watch the browser console for warnings. Browsers log a message for cookies that will be rejected or that rely on the default, and those messages are the fastest inventory of what needs changing.
Test the cross-site flows specifically: an embed, a payment redirect, a single sign-on return. Those are where a wrong value breaks something rather than merely weakening it.
How to fix it
Declare the value on every cookie rather than relying on the default. The default is a browser decision; the declaration is yours.
Set `Lax` as the baseline in the session middleware, and override to `Strict` or `None` per cookie where a reason exists.
Pair every `None` with `Secure`, and confirm the cookie is actually stored afterwards rather than assuming the declaration took effect.
Keep CSRF tokens on state-changing requests. `SameSite` narrows the attack surface; the token is what closes it.
Audit third-party cookies set on your domain, which will follow their own policy regardless of yours.
How VeriFixScan detects it
`cookies.samesite` reads the attribute on every cookie observed on the crawled pages and reports the ones with no declaration, along with the value where one is present.
`cookies.samesite_none_secure` reports the specific failure of `None` without `Secure`, which is the combination browsers reject outright.
`cookies.third_party` identifies cookies set by other origins, whose policy is theirs rather than yours.
`cookies.inventory` gives the full attribute set per cookie, so the three attributes are read together rather than as three separate findings about one cookie.
Frequently asked questions
- Browsers default to Lax. Do I still need to declare it?
- Yes. A default is a browser decision that can change and that differs in edge cases, including a short grace period for recently set cookies on cross-site POSTs. A declaration is yours and is stable.
- Why is my SameSite=None cookie not being set?
- Because it is missing the Secure attribute. Browsers reject SameSite=None without Secure outright, so the cookie is never stored and nothing appears in the request to debug.
- Does SameSite replace CSRF tokens?
- No. It reduces the attack surface substantially and is not a complete defence. Keep tokens on state-changing requests.
Sources
Related
VeriFixScan crawls a site and applies its checks to every page it reaches, keeping the evidence behind each finding. Scanning one website is free.
Scan a website