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