Cookies: attributes, SameSite and partitioning
SameSite asks which site started the request, and a site is a registrable domain, not an origin.
Predict first
Before you read: predict
ExamHall's stand-alone candidate app on https://exam.examhall.test has set __Host-session=…; Secure; HttpOnly; Path=/; SameSite=Lax, and the candidate is signed in. Then four requests reach exam.examhall.test:
- On the university course page
https://lms.uni.test, the candidate clicks a link to/attempts(GET). - The same course page shows an
<img>from/logo.gif. - The proctor console on
https://proctor.examhall.testshows an<img>from/status.gif. - A form on the course page POSTs to
/attempts/start.
Which requests carry the session cookie?
The 30-second version
The browser, not the page, attaches cookies. Domain widens a cookie to a domain plus subdomains, Path narrows it; Secure needs HTTPS; HttpOnly hides it from script. SameSite compares sites (scheme plus registrable domain), not origins: Strict sends only on same-site requests, Lax also on cross-site top-level GET navigations, None everywhere, with Secure. Partitioned keeps one jar per top-level site.
SameSite asks which site started the request, and a site is a registrable domain, not an origin.
How it works
Step 1 of 11
Set-Cookie stores a name, a value and attributes. On every later request the browser decides whether to attach it; page code never chooses.Analogy: A diplomatic bag
A ministry hands its envoy sealed envelopes. On every trip the envoy, not the people she visits, reads each envelope's labels and decides whether it travels.
| In the analogy | In the browser |
|---|---|
| "For the main building only", or "for the main building and all its annexes" | host-only, or Domain (step 2) |
| "Armoured car only"; "never show to local staff" | Secure; HttpOnly (step 3) |
| The ministry and its annexes are one government, whichever building | one site, many origins (step 5) |
| Carried only on errands the ministry itself started | Strict (step 6) |
| Also carried when a foreign office invites her to visit in person, to look, not to sign | Lax: a cross-site top-level GET (step 7) |
| Carried on any errand, but only in the armoured car | None needs Secure (step 8) |
| A separate bag for each host country she serves in | Partitioned (step 10) |
Breaks down when: an envoy judges an errand's purpose; the browser checks only sites and the request's kind. Wrong inference: "only legitimate visits carry a Lax cookie." A forged GET navigation carries it too (step 11).
Contrasting case: a passport the envoy shows only when her own staff hand it over. That is a token the page stores and sends in a header itself: page code decides, and script can read it.
In real terms: for each request the browser filters the jar by host, path and scheme, then compares the request's initiating site with the cookie's site under its SameSite mode, and, for a partitioned cookie, the top-level site with its partition key.
Examples, run for real
4.1 ExamHall stand-alone: which cookies the browser keeps
The app tries six Set-Cookie lines on exam.examhall.test; the fourth arrives over plain HTTP.
https __Host-session=s1; Secure; HttpOnly; Path=/; SameSite=Lax -> stored
https __Host-legacy=s2; Secure; HttpOnly; Path=/; Domain=examhall.test; SameSite=Lax -> rejected
https widget=w1; Path=/; SameSite=None -> rejected
http pref=p1; Secure; Path=/; SameSite=Lax -> rejected
https theme=dark; Secure; Path=/; SameSite=Lax -> stored
https draft=d1; Secure; Path=/attempts; SameSite=Lax -> stored
request GET /attempts/7 carries: __Host-session, draft, theme
document.cookie on /attempts/7: "draft=d1; theme=dark"
request GET /help carries: __Host-session, theme
A Domain breaks the __Host- rule (step 4); None without Secure and Secure over HTTP are refused (steps 3, 8). Script never sees the HttpOnly session, yet every request carries it (step 3). draft stays under /attempts (step 2).
4.2 ExamHall in the LMS iframe: EXH-657 and MR !977
EXH-657: embedded in a university LMS, ExamHall showed "Signed out" on every question. The LMS launches ExamHall inside an iframe, whose response sets the cookie; the iframe then loads a question. The same candidate later opens a second university's LMS.
third-party cookies allowed (default profile):
Lax (the stand-alone cookie) | stored: nothing | in lms.uni.test: not sent | in lms.otheruni.test: not sent
None; Secure | stored: __Host-embed | in lms.uni.test: sent | in lms.otheruni.test: sent
None; Secure; Partitioned | stored: __Host-embed [key uni.test] | in lms.uni.test: sent | in lms.otheruni.test: not sent
third-party cookies blocked (DevTools Protocol cookie controls):
Lax (the stand-alone cookie) | stored: nothing | in lms.uni.test: not sent | in lms.otheruni.test: not sent
None; Secure | stored: nothing | in lms.uni.test: not sent | in lms.otheruni.test: not sent
None; Secure; Partitioned | stored: __Host-embed [key uni.test] | in lms.uni.test: sent | in lms.otheruni.test: not sent
A cross-site iframe is not a top-level navigation, so Lax is never even stored (step 7). Plain None works only while third-party cookies are allowed, and then follows the candidate into every LMS. MR !977 issues a separate partitioned embed cookie: one jar per university, surviving third-party blocking (step 10). The stand-alone cookie stays Lax.
4.3 Proof domain, ticketing: four SameSite modes, five requests
A fan forum on another site makes five requests to tix.ticketco.test, which holds four cookies differing only in SameSite:
request from fanforum.test | strict | lax | none | unset
link (top-level GET) | - | sent | sent | sent
GET form (top-level) | - | sent | sent | sent
POST form (top-level) | - | - | sent | sent
<img> | - | - | sent | -
<iframe> | - | - | sent | -
unset cookie as stored by Chromium: sameSite=Lax
Strict goes nowhere cross-site (step 6); Lax only on top-level GETs (step 7). The unset column is the draft's Lax default with its 2-minute POST allowance, as Chromium implements them (step 9): a one-off run printed unset for a 1-second-old cookie and none at 125 seconds. Firefox does not default to Lax (a missing SameSite acts as None), though its default third-party partitioning changes the embedded rows; Firefox was not run.
Picture it

Try it
Answer first, then check. Nothing is saved.
Question 1 of 5
A chat app keeps its session in __Host-chat=…; Secure; HttpOnly; Path=/; SameSite=Strict on https://app.chatco.test. A signed-in user reads an invite in webmail on https://mail.webmail.test and clicks its link to the room page https://app.chatco.test/room/42. The room page then loads its own avatar image from https://app.chatco.test/avatar.gif.
Which of these two requests carries the session cookie?
Question 2 of 5
A ticket site on https://tix.ticketco.test moves a ticket on GET /tickets/transfer?to=…. Its session cookie is __Host-tix=…; Secure; HttpOnly; Path=/; SameSite=Lax. A signed-in fan opens a page on https://evil.test that tries three things: a script redirect (location.href = …/tickets/transfer?to=mallory), an <img> with that URL, and a hidden form that POSTs to it.
Which attempts carry the session cookie?
Question 3 of 5
https://shop.catalog.test has set four cookies: sid=…; Secure; HttpOnly; Path=/, cart=3; Secure; Path=/cart, cur=EUR; Secure; Path=/ and ab=B; Secure; Domain=catalog.test; Path=/, all SameSite=Lax. Script on https://shop.catalog.test/products/42 reads document.cookie.
Which cookie names does it see?
Question 4 of 5
StreamCo's viewer app on https://watch.streamco.test keeps its session in __Host-sid=…; Secure; HttpOnly; Path=/; SameSite=Strict. Creators publish their own HTML pages on https://creators.streamco.test. One creator page auto-submits a hidden form that POSTs to https://watch.streamco.test/account/email, and signed-in viewers who open it get their account email changed. The team says Strict should have stopped this.
Which is the bug?
Question 5 of 5
A shop on https://shop.merchant.test sets pay_ref=…; Secure; HttpOnly; Path=/pay; SameSite=Lax, then sends the buyer to the bank's check page on https://acs.bank.test. When the check is done, the bank's page auto-submits a form that POSTs to https://shop.merchant.test/pay/return. The shop never finds pay_ref there, so the payment cannot be matched.
Which change makes pay_ref arrive?
Go deeper
Pro tips
- Log
Sec-Fetch-Siteon the server to see how the browser classified a request. It readssame-origin,same-site,cross-siteornone.- When it helps: a cookie "sometimes" goes missing and you need to know whether the browser saw the request as cross-site (steps 5–7).
- Status: RAN (Chromium 153.0.8010.12).
- In DevTools, open the request in the Network panel and check its Cookies tab, with "show filtered out request cookies" ticked. Chrome lists each withheld cookie with the reason, such as its SameSite setting.
- When it helps: you need to know which attribute withheld a cookie.
- Status: DOC (Chrome DevTools: Network features reference); not run in this session (headless).
Source
- It does not define
Partitioned. Valid as of 2026-09-27. - HTML Standard, Sites: https://html.spec.whatwg.org/multipage/browsers.html · fetched 2026-09-27. A site is obtained from an origin's scheme and registrable domain; "same site" compares scheme too, and a port difference changes the origin, not the site. Valid as of 2026-09-27.
- MDN, Set-Cookie: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie · fetched 2026-09-27. Domain and host-only, Path prefix matching, Secure (with its localhost exception), HttpOnly, Strict/Lax/None, None requiring Secure, Partitioned requiring Secure,
__Host-rules. MDN's Cookies guide calls Lax the default when SameSite is missing without naming browsers; this lesson follows the executed Chromium run and the Firefox bug below instead. Valid as of 2026-09-27. - MDN, Cookies Having Independent Partitioned State (CHIPS): https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies · fetched 2026-09-27. The partition key is the site (with scheme) of the top-level URL; Partitioned cookies must be Secure. Valid as of 2026-09-27.
- Chromium, SameSite FAQ: https://www.chromium.org/updates/same-site/faq/ · fetched 2026-09-27. The Lax + POST exception covers cookies up to 2 minutes old on cross-site POSTs and is "purely a temporary solution". Valid as of 2026-09-27.
- Mozilla bug 1617609, "Enable sameSite=lax by default": https://bugzilla.mozilla.org/show_bug.cgi?id=1617609 · fetched 2026-09-27. Resolved WONTFIX; comment 26: "Firefox does not implement Lax by default." Valid as of 2026-09-27.
- Curriculum sources (not re-quoted): RFC 6265, https://www.rfc-editor.org/rfc/rfc6265 (obsoleted in practice by the draft above; it has no SameSite).
Related lessons
Checked against the tools and docs as of 2026-09-27.