CHIPS and partitioned cookies: the post-third-party-cookie identity model
A third-party cookie has one defining property: it is sent on a request to a host that is not the page the user is looking at. An analytics SDK on news.example sets a cookie on tracker.example, and that same tracker.example cookie rides along when the user later visits shop.example that also embeds the SDK. One host, one cookie, every site the user touches. That single shared jar is the mechanism that built cross-site tracking, and it is the thing browsers spent the last decade trying to kill without breaking the embedded chat widget, the CDN-pinned session, and the SSO iframe that all depend on a cookie surviving across an origin boundary.
CHIPS is the answer that shipped. Not a privacy framework, not a sandbox of replacement APIs, just one cookie attribute and a change to how the browser keys its cookie store. Set Partitioned and the browser stops giving you one cookie jar per host. It gives you one jar per host per top-level site. The tracker on news.example and the tracker on shop.example are now two cookies with the same name that can never see each other. This post is about exactly how that double-keying works, what it lets through, what it blocks, and why the spec that was meant to be a small piece of a much larger Privacy Sandbox outlived the rest of it.
The sections below cover the double-keyed cookie jar and how the partition key is computed, the exact Set-Cookie syntax and why Secure is mandatory, the cross-site ancestor chain bit that changed the key in 2025, the per-partition storage budget, how this lines up with general storage partitioning, the cross-browser state, and what changed when Google walked away from deprecating third-party cookies entirely.
The double-keyed cookie jar
A normal cookie is keyed on what the spec calls the host key: the domain or hostname that set it, plus path and a few flags. When the browser builds the Cookie header for an outgoing request, it walks its store, finds every cookie whose host key matches the request target (subject to SameSite, Secure, path, and the rest), and attaches them. The top-level site the user is on never enters that calculation. That is precisely why a third-party cookie follows the user everywhere: the store has no notion of “which site was this cookie for.”
CHIPS adds a second key. A cookie set with the Partitioned attribute is stored against both its host key and a new partition key. The Privacy Sandbox documentation defines it plainly: a cookie’s partition key is the site, meaning the scheme and registrable domain, of the top-level URL the browser was visiting when the request that set the cookie was made. If support.chat.example is embedded in retail.example and sets a partitioned cookie, the partition key is ("https", "retail.example"). The same widget embedded in news.example sets a cookie under partition key ("https", "news.example"). Two cookies, same host, same name, two jars.
On retrieval the browser does the obvious thing. The IETF draft by Dylan Cutler of Google, first published November 2022, describes the algorithm: if a stored cookie’s partition key is null it is an old-style unpartitioned cookie and the partition check is skipped; otherwise the cookie is only included in the request if its partition key is same-site with the partition key derived from the current top-level document. A retail.example partition cookie simply is not a candidate when the top-level site is news.example. Nothing blocks it after the fact with a policy check; it never enters the set the browser considers in the first place.
The privacy property falls straight out of this. Cross-site tracking works because a tracker can recognise the same cookie value on two unrelated sites and stitch the visits into one profile. Under CHIPS the cookie value on news.example and the cookie value on shop.example are different storage entries the tracker set independently, and there is no request on which both are present. The browser never hands the tracker a stable identifier that spans the two sites. What survives is per-site state, which is what the legitimate use cases needed in the first place.
The Partitioned attribute and why Secure is not optional
The opt-in is a single attribute on the Set-Cookie response header. The canonical form, from both MDN and the Privacy Sandbox docs, is:
Set-Cookie: __Host-id=value; Secure; Path=/; SameSite=None; Partitioned;Four things are doing work there and it is worth being precise about which are required and which are recommended. Partitioned is the opt-in itself. Secure is mandatory: the CHIPS proposal states the user agent must reject any cookie set with Partitioned that does not also include Secure, and the IETF draft makes this normative in its store-model algorithm, where a partitioned cookie whose secure-only flag is false is ignored entirely. There is no partitioned cookie over plain HTTP. The reasoning in the draft’s security section is that partitioned state is meant to be a clean, isolated identity primitive, and an unencrypted one would reintroduce the network-injection problems the rest of the cookie hardening work spent years closing off.
SameSite=None is the practical pairing rather than a hard requirement of the attribute. A partitioned cookie that needs to be sent in a cross-site context, which is the whole point of an embedded third party, has to declare SameSite=None or SameSite’s default lax behaviour will keep it off cross-site requests before partitioning ever enters the picture. The two attributes solve different problems. SameSite decides whether a cookie is sent on cross-site requests at all; Partitioned decides which jar it is read from once it is allowed to be sent. The SameSite and partitioning post covers the SameSite half of this in detail, and CHIPS sits directly on top of it.
__Host- is recommended, not required. The prefix is an older cookie-hardening convention that forces a cookie to be Secure, to have Path=/, and to carry no Domain attribute, which binds it to the exact host that set it rather than the registrable domain. For a partitioned cookie that you do not need to share across subdomains, the prefix closes a small attack surface for free. The MDN guidance and the Chrome docs both recommend it without making it a condition of partitioning.
There is a deliberate asymmetry in the model worth flagging. A site cannot promote an existing unpartitioned cookie into a partition, and it cannot read a partitioned cookie from outside its partition by dropping the attribute. The Partitioned flag is a property of the stored cookie at write time. Setting a partitioned cookie creates a fresh entry under the current partition key; it does not reach into or merge with the global unpartitioned jar. That one-way wall is the reason partitioning can be trusted as an isolation boundary rather than a soft hint.
JavaScript gets the same control. document.cookie accepts the attribute in a write string, and the CookieStore API was extended with a partitioned boolean on CookieInit, a partitioned field on the CookieListItem you read back, and a partitioned option on the delete path, so script-set cookies participate in the same double-keying as header-set ones.
What the partition key actually contains, and the ancestor bit
For the first two years of CHIPS the partition key was exactly what the early spec said: the site of the top-level document, scheme and registrable domain, nothing else. Nesting depth did not matter. A cookie set by a third party three iframes deep still landed in the top-level site’s partition. That is clean and easy to reason about, and it is also where a subtle security gap lived.
Consider the frame chain A → B → A. The top-level site is A. It embeds a cross-site iframe from B. That B frame then embeds an iframe back to a document on A. Under the original key, the innermost A frame and the top-level A page share a partition, because both resolve to top-level site A. So a cross-site B document could embed an arbitrary credentialed endpoint of A, in the inner frame, and that endpoint would receive A’s partitioned cookies. B did not get the cookie values, but it got to drive authenticated requests into A’s own partition without A ever granting access through the Storage Access API or CORS. A confused-deputy gap, narrow but real.
Chrome’s fix, shipped in M125 in April 2025, adds a cross-site ancestor chain bit to the partition key. The bit is set when any document between the current frame and the top-level document is cross-site to the top-level site. With the bit in the key, the inner A frame in A → B → A no longer shares a partition with the top-level A page, because its ancestor chain passed through cross-site B and flipped the bit. Same top-level site, different partition. The cross-site embed can no longer reach the top-level site’s credentialed partition by bouncing through itself.
Chrome’s own measurement put the affected traffic at roughly 0.2% of first-party partitioned cookies used in A → B → A contexts, which is why the change shipped without a long deprecation runway. The important point for anyone reasoning about partition identity now is that the key is no longer just the top-level site. It is the top-level site plus a boolean that records whether the path to the current frame crossed a site boundary. This also aligned cookie partitioning with how Chrome already keyed other partitioned storage, which is the subject of a later section. The exact in-memory representation of the bit is an implementation detail of Chrome’s CookiePartitionKey; the observable behaviour is the partition split shown above.
The storage budget: 10 KiB and a cookie cap
Partitioning multiplies cookie storage. One unpartitioned cookie is one entry. The same cookie set as partitioned across a thousand visited top-level sites is a thousand entries. Left unbounded that is a denial-of-service vector against the user’s own disk and a way to fingerprint by filling storage, so the spec caps it.
The IETF draft puts the limit as a recommendation: a user agent may limit a domain’s cookies to 10 kilobytes per top-level partition. The CHIPS repo adds the empirical justification, that Chrome data suggests ten cookies per partition satisfies around 99% of existing cross-site cookie use cases. Chrome’s shipped numbers, per the Privacy Sandbox docs, are a maximum of 180 cookies per partition with a 10 KiB per-partition-per-embedded-site budget. The byte limit is the binding one in practice; a single embedded third party gets at most 10 KiB of partitioned cookie storage within any one top-level site’s partition, independent of what the same third party stores in other partitions.
That independence matters and it is deliberate. The draft’s privacy section is explicit that partitioned cookies must use separate per-domain limits per partition, because a shared limit would be a cross-partition side channel. If filling storage in the news.example partition could evict or constrain cookies in the shop.example partition, a tracker could probe one partition’s state by observing eviction in another, reconstructing exactly the cross-site signal partitioning is meant to destroy. Per-partition budgets keep the partitions from leaking through their own quota accounting.
Clearing follows the same partition-scoped logic. When a third party sends Clear-Site-Data, the browser clears that third party’s cookies in the current top-level site’s partition only; the draft is explicit that it must not touch the same third party’s cookies in other partitions. A widget logging a user out on shop.example does not log them out of the widget on news.example, because those are genuinely separate sessions now.
The retrieval algorithm, step by step
It helps to walk the actual decision the browser makes when it assembles the Cookie header for an outgoing request, because the partition check is one branch inside a longer pipeline and it is easy to misattribute behaviour to partitioning that is really SameSite or Secure doing the work. The following is a faithful description of the retrieval logic in the IETF draft, written as illustrative pseudocode rather than a copy of any one engine’s source. It shows where the partition key enters and nothing more.
for cookie in cookie_store: if not host_key_matches(cookie, request.host): continue # wrong domain if cookie.secure_only and request.scheme != "https": continue # Secure gate if not path_matches(cookie, request.path): continue # path scope if not same_site_ok(cookie, request, navigation): continue # SameSite gate if cookie.partition_key is not None: # the CHIPS branch req_pk = (top_level_site, cross_site_ancestor_bit) if cookie.partition_key != req_pk: continue # wrong partition include(cookie)The order matters for reasoning about behaviour. By the time the partition branch runs, the cookie has already passed the host, scheme, path, and SameSite filters. So a partitioned cookie that is missing from a request might be missing for any of those reasons, not because partitioning excluded it. The single most common confusion is a developer setting Partitioned without SameSite=None, watching the cookie vanish on cross-site requests, and blaming the partition logic, when the cookie never reached the partition branch at all because SameSite’s lax default dropped it two steps earlier. The two gates are independent and both have to pass.
The partition comparison itself is a strict equality on the full key, top-level site and ancestor bit together. There is no notion of a partition being a superset or a parent of another. A cookie set in the (retail.example, bit=0) partition is invisible in (retail.example, bit=1) despite the matching site, which is exactly the A → B → A isolation. This strictness is what makes the partition a clean boundary to reason about: either the keys are byte-for-byte the same and the cookie is a candidate, or they are not and it does not exist for this request. There is no fuzzy matching to exploit.
For anyone diffing what a real browser sends against what an HTTP client sends, this is the practical takeaway. A partitioned cookie’s presence on the wire is fully determined by the top-level browsing context, which a bare HTTP client does not have. There is no top-level site, no ancestor chain, no partition key to compute, so a scripted client reconstructing a session has to know which partition a captured cookie belonged to and replay it only in the matching context. The cookie value alone is not enough; the partition it was bound to is part of its identity now. Tooling that captures cookies from a live browser, like the request-capture flow discussed in the session and cookie management across a proxy fleet post, has to record the partition key alongside the value or the replay will silently send the cookie into the wrong jar. Chrome DevTools surfaces this directly: under Application, Storage, Cookies, the details panel shows a Partition Key column, and unpartitioned cookies display an empty key while partitioned ones carry their top-level site.
Where CHIPS sits in storage partitioning
CHIPS did not arrive alone. Chrome partitions most of its client-side storage by a StorageKey, and cookies were the last major piece to be brought into line. The storage partitioning docs list what is keyed this way: Local Storage and Session Storage, IndexedDB, Cache Storage, the Origin Private File System, and the Storage Buckets API. Each of those is scoped so that an embedded third party gets a different storage instance per top-level site, the same shape as a partitioned cookie jar.
The StorageKey is composed of the frame’s origin, the top-level site, and the same cross-site ancestor chain bit that CHIPS adopted in 2025. That adoption was the point. Before M125, cookie partitioning keyed only on top-level site while the StorageKey already carried the ancestor bit, so an A → B → A inner frame got one partition for its cookies and a different one for its IndexedDB. Adding the bit to the cookie partition key made the two consistent, so a piece of embedded code now sees the same partition boundary across cookies and structured storage. For anyone building or detecting cross-site state, that alignment is the useful invariant: per-partition identity is now a single boundary, not two slightly different ones.
The contrast with cookies is that the storage APIs are partitioned by default, with no opt-in attribute. There is no Partitioned-equivalent flag on IndexedDB. A third-party iframe’s IndexedDB has been partitioned by top-level site in Chrome for some time regardless of what the developer asks for. Cookies are the exception that kept an opt-in, because the unpartitioned third-party cookie was load-bearing for so much of the web that flipping it to partitioned-by-default would have broken things in ways the storage APIs did not. The opt-in is the migration path: set Partitioned, get a cookie that survives third-party-cookie blocking, and do not depend on the legacy shared jar that browsers are free to take away.
Cross-browser state
CHIPS is one of the rare privacy features that landed with all three engines on side. Chrome shipped it in version 114, with Edge following at 114 on the same Chromium base. The standards positions were filed early and were positive across the board. Mozilla labelled its position positive in August 2022, and Firefox shipped support in version 141. WebKit’s position, filed the same month with John Wilander and Ted O’Connor assigned, was support, tagged with a performance concern to work through rather than an objection, and Safari shipped it in 26.2. MDN’s compatibility data now records partitioned cookies as a Baseline feature newly available across the latest browser versions as of December 2025.
The reason all three converged is that CHIPS matched where each was already heading. Safari’s Intelligent Tracking Prevention had been partitioning and then outright blocking third-party cookies for years; an explicit opt-in attribute gave embedded services a sanctioned way to keep per-site state instead of having it silently dropped. Firefox’s Total Cookie Protection, the productised name for its dynamic state partitioning, already double-keyed third-party cookies by top-level site automatically. CHIPS is close to a standardised, opt-in version of what Firefox does implicitly. The MDN guidance states the practical difference directly: Firefox partitions third-party cookie storage by default, while CHIPS makes partitioning an explicit, interoperable opt-in that works the same way across engines. For a developer the recommendation is to use the Partitioned attribute rather than relying on any one browser’s default partitioning, because the attribute is the portable contract.
What changed when Privacy Sandbox went away
CHIPS was conceived as one component of the Privacy Sandbox, the suite Google built to replace third-party-cookie functionality before deprecating the cookies themselves. The plan was that the unpartitioned third-party cookie would be removed from Chrome and developers would migrate to partitioned cookies for per-site state, to the Topics API for interest signals, to the Protected Audience API for remarketing, and to Attribution Reporting for conversion measurement. That deprecation never happened.
Google delayed the removal repeatedly, and in July 2024 it abandoned the plan, saying it would not phase out third-party cookies in Chrome and would instead offer a user choice prompt. By April 2025 even the prompt was dropped, leaving Chrome’s existing cookie controls in place. In October 2025 Google retired the remaining Privacy Sandbox advertising APIs, Topics, Protected Audience, and Attribution Reporting, on both Chrome and Android. The sandbox that CHIPS was supposed to be part of is gone.
CHIPS outlived it, and the reason is structural. Topics and Protected Audience were attempts to rebuild ad-tech functionality inside the browser, and they carried Google-specific design and adoption baggage that made the antitrust and industry pushback fatal. CHIPS asked for none of that. It is a cookie attribute with a clear security story, positive positions from Mozilla and Apple, and a shape that matched what those browsers already shipped. It does not depend on third-party cookies being removed to be useful. The instant any browser blocks third-party cookies, by default like Safari and Firefox or by user setting in Chrome, a Partitioned cookie is the supported way for an embedded service to keep working. That value does not evaporate because Chrome decided to keep the legacy cookie alive.
So the current state is a split. The unpartitioned third-party cookie is not being killed in Chrome, which means the cross-site identity it enables persists for any site and any user who has not turned it off. At the same time CHIPS is Baseline, interoperable, and the default-safe choice for new embedded state, because two of the three major engines block the unpartitioned cookie out of the box and the third lets users do the same. A developer shipping an embedded widget today writes Partitioned not because Chrome forced the migration but because it is the only cookie that behaves consistently across all four browsers. The deprecation that was supposed to make CHIPS mandatory fell through; the interoperability that made CHIPS portable is what kept it.
Closing: a small attribute that did its one job
CHIPS is worth studying precisely because it is narrow. It changed one thing, the key the cookie store uses, from a single host key to a host key plus a partition key plus, since 2025, a cross-site ancestor bit. Everything else falls out of that. The privacy property, no stable cross-site identifier, is a consequence of the keys never being compared across top-level sites. The security property, no confused-deputy reach into a top-level partition, is the ancestor bit. The denial-of-service mitigation is the per-partition 10 KiB budget, kept independent so the quota itself does not leak. None of it required new cryptography or a new request flow, just a more specific answer to the question “whose cookie is this.”
The contrast with the rest of the Privacy Sandbox is the lesson. The APIs that tried to relocate an entire advertising mechanism into the browser collapsed under their own ambition and the scrutiny that came with it. The attribute that did one legible thing, with a security story every engine could agree on, is in all four browsers and graded Baseline. Third-party cookies are still alive in Chrome, so CHIPS is not the forced replacement it was meant to be. It is something more durable instead: the one piece of identity plumbing that every browser ships the same way, sitting ready for the day any given user turns the old cookie off.
Sources & further reading
- Privacy Community Group / Cutler, D. (2022-2025), CHIPS: A proposal for a cookie attribute to partition cross-site cookies by top-level site — the canonical explainer, design principles, opt-in rationale, and per-partition limit reasoning.
- Cutler, D. / IETF (2022), Cookies Having Independent Partitioned State (draft-cutler-httpbis-partitioned-cookies-01) — the formal store/retrieve algorithms, the Secure requirement, and the 10 KiB recommendation.
- Google Privacy Sandbox (2025), Cookies Having Independent Partitioned State (CHIPS) — partition key definition, Set-Cookie examples, Chrome’s 180-cookie / 10 KiB limits, and DevTools inspection.
- MDN Web Docs (2025), Cookies Having Independent Partitioned State (CHIPS) — attribute syntax, Baseline status, and the comparison with Firefox state partitioning.
- Google Privacy Sandbox (2025), Storage partitioning — which storage APIs are keyed by StorageKey and how the ancestor bit composes the key.
- Chromium blink-dev (2025), Intent to Ship: Adding Cross-site Ancestor Chain Bit to CHIPS Partition Key — the A→B→A confused-deputy gap, the fix, and the 0.2% impact measurement.
- Mozilla Standards Positions (2022), CHIPS position: positive — Firefox’s stance and its relation to Total Cookie Protection.
- WebKit Standards Positions (2022), CHIPS position: support — Safari’s stance, assignees, and the noted performance concern.
- Digital Commerce 360 (2024), Google ends its third-party cookies deprecation plans for Chrome — the July 2024 reversal that left third-party cookies in Chrome.
- Usercentrics (2025), Google Privacy Sandbox officially shuts down: what it means — the 2025 retirement of Topics, Protected Audience, and Attribution Reporting.
Further reading
The cookie and identity layer: SameSite, partitioning, and the third-party-cookie death
Traces the HTTP cookie from a 1994 shopping-cart hack to the web's identity layer: how SameSite reshaped it, why the third-party-cookie phase-out collapsed in 2024-2025, and what partitioning leaves behind.
·18 min readEncrypted Client Hello (ECH): what it hides and what it doesn't
How ECH encrypts the inner ClientHello, including SNI, with an HPKE key fetched from DNS, what the outer ClientHello still leaks, and where deployment actually stands now that RFC 9849 has shipped.
·20 min readThe Battery Status API and the privacy disaster that got it deprecated
Traces how four read-only battery properties became a cross-site tracking vector, the 2015 Olejnik research that proved it, the in-the-wild scripts Princeton caught, and the Firefox and WebKit removals that followed.
·21 min read