How to check SameSite cookie attribute
Read the Set-Cookie lines and pull the attribute out: curl -s -o /dev/null -D - https://example.com/ | grep -o -i -E 'samesite=[a-z]+'. Expect Strict, Lax or None on every cookie you own. A cookie with no SameSite at all is treated as Lax by current browsers, which is a default, not a decision.
Why check this
SameSite decides whether a cookie rides along on a request that a different site started. It is the cheapest defence against cross-site request forgery and the most common cause of a payment callback or an embedded widget losing its session. Run this check whenever a flow crosses an origin boundary: a hosted checkout returning to your domain, single sign-on, an iframe embed, or an OAuth redirect back from an identity provider.
Prerequisites
- curl 8.0 or later with GNU grep and sed. See the curl manual.
- Node 22 if you want a response carrying every value at once. Save as
cookies.mjsand runnode cookies.mjs:
import http from 'node:http';
http.createServer((req, res) => {
res.setHeader('set-cookie', [
'sid=a1; Path=/; Secure; HttpOnly; SameSite=Strict',
'csrf=b2; Path=/; Secure; SameSite=Lax',
'embed=c3; Path=/; Secure; SameSite=None',
'broken=d4; Path=/; SameSite=None',
'legacy=e5; Path=/',
]);
res.writeHead(200, { 'content-type': 'text/plain' });
res.end('ok\n');
}).listen(8792, '127.0.0.1');
- MDN on SameSite for which request types each value allows.
Steps
- Step 1.
Count the values a real response uses.
curl -s -o /dev/null -D - https://www.cloudflare.com/ | grep -i '^set-cookie:' | grep -o -i -E 'samesite=[a-z]+' | sort | uniq -c3 SameSite=Lax 1 SameSite=NoneFour values across five cookies. The count that is missing from this output is the one step 2 finds.
- Step 2.
List the cookies that carry no
SameSiteattribute at all.curl -s -o /dev/null -D - https://www.cloudflare.com/ | grep -i '^set-cookie:' | sed -E 's/^[Ss]et-[Cc]ookie: ([^=]+)=[^;]*/\1/' | sed -E 's/ ?(Expires|Path|Domain|Max-Age)=[^;]*;?//gI' | grep -vi 'SameSite'_ga;One cookie with no attribute. Browsers apply
Laxto it, so its behaviour depends on the browser version rather than on the server. - Step 3.
Start the local server from the prerequisites and read every shape in one response.
curl -s -o /dev/null -D - http://127.0.0.1:8792/ | grep -i '^set-cookie:'set-cookie: sid=a1; Path=/; Secure; HttpOnly; SameSite=Strict set-cookie: csrf=b2; Path=/; Secure; SameSite=Lax set-cookie: embed=c3; Path=/; Secure; SameSite=None set-cookie: broken=d4; Path=/; SameSite=None set-cookie: legacy=e5; Path=/ - Step 4.
Find the combination a browser rejects outright.
curl -s -o /dev/null -D - http://127.0.0.1:8792/ | grep -i '^set-cookie:' | grep -i 'samesite=none' | grep -vi 'secure'set-cookie: broken=d4; Path=/; SameSite=NoneSameSite=NonewithoutSecureis not a weaker cookie, it is no cookie. Browsers discard the wholeSet-Cookieline, so the symptom is a session that never starts. - Step 5.
Open the site in Chrome, press F12, then the Application tab, Storage, Cookies, and select the origin.
The grid has a SameSite column with the value per cookie, and blank where the attribute is absent. That blank is the one thing curl cannot tell you apart from the browser default, because the default is applied inside the browser and never appears on the wire. Stop the local server with Ctrl+C when you are done.
How to read the result
| What you see | What it means | What to do |
| --- | --- | --- |
| SameSite=Strict on a session cookie | Never sent on a request another site started, top-level navigation included | Check that arriving from an external link still shows the user as logged in. |
| SameSite=Lax | Sent on top-level GET navigation, withheld on cross-site POST and subresources | The right default for a session cookie. |
| SameSite=None; Secure | Sent on every cross-site request | Only for cookies an embed or a third-party callback needs. Pair it with a CSRF token. |
| SameSite=None with no Secure | The browser drops the cookie entirely | Release blocker. The session silently fails to start. |
| No SameSite attribute | The browser applies Lax | Write the value down explicitly so behaviour does not depend on the client. |
| SameSite=Strict on a cookie used by an iframe | The embed never receives it | Split the embed onto its own cookie with None; Secure. |
Common mistakes
What to check next
- How to check if cookies are secure and HttpOnly: the two attributes on the same line, and why
Nonedepends on one of them. - How to check cookie flags with curl: why the cookie jar cannot answer a SameSite question.
- How to test CORS with curl: the other half of a cross-origin failure, on the response side.
- How to check HTTP response headers with curl: the flags every step here relies on.
- Security headers checker: the response header set around the cookie.
FAQ
How to check SameSite cookie in Chrome?
Press F12, open the Application tab, then Storage, Cookies, and pick the origin. The SameSite column shows the value per cookie and stays blank when the server sent no attribute.
What does the browser do when SameSite is absent?
It treats the cookie as Lax. The default is applied client side, so the header on the wire still shows nothing, and two browser versions can disagree about the same response.
Why does SameSite=None need Secure?
A cookie that travels on every cross-site request is the one that most needs TLS. Browsers enforce the pairing by rejecting the Set-Cookie line when Secure is missing, as step 4 shows.
Does SameSite replace a CSRF token?
No. Lax still allows top-level GET navigation, and any flow that needs None has the attribute switched off by design. Keep the token.
Can curl prove SameSite is working?
No. curl reads what the server sent. Enforcement happens in the browser, which is where the check ends.
Verified
Verified by Maks Vernycurl 8.21.0node 22.23.2
Each output block is what the command above it printed on that date, on the host named in the step. Figures read from a live site move between runs. Compare the shape of the answer rather than the digits, and see the methodology for how a page is re-verified.
Related on this site
intermediate6 minpublished updated Maks Verny