Storage partitioning keys browser state by the embedded site and the top-level page, so one widget on two sites gets two separate storage buckets.
For most of the web's history, a browser answered the question "which storage does this code get?" by looking at one thing: the origin the code came from. A tracking widget embedded on a hundred sites therefore read and wrote the same cookie jar on all hundred, and that shared jar is what made cross-site tracking so cheap. Storage partitioning changes the question. Storage is now keyed by two things — the origin that owns it and the top-level site you are actually visiting — so the same embedded widget on two different sites gets two unrelated buckets that cannot see each other.
Key Takeaways
- Double-keying is the whole idea. Instead of keying state by the resource's origin alone, the browser keys it by (resource origin, top-level site). MDN's State Partitioning guide describes Firefox as double-keying "all client-side state by the origin of the resource being loaded and by the top-level site."
- The same embed on two sites means two buckets, not one. A tracker embedded in
A.exampleandB.examplecan no longer store an ID in one place and read it back in the other; the data it sees on A differs from what it sees on B. - "Cookies" is only part of the surface. Partitioning also covers
localStorage,sessionStorage, IndexedDB, the Cache API, service workers, and network state such as the HTTP cache — and the engines do not all partition the same things in the same way. - The Storage Access API is the deliberate escape hatch. An embedded frame can call
requestStorageAccess()after a user gesture to regain its normal first-party cookies, but the grant is scoped to one (top-level site, embedded site) pair, not switched on globally. - Partitioning breaks state-based cross-site tracking, not tracking itself. A fingerprint needs no storage, so there is nothing to partition — which is why this change made fingerprinting more valuable to trackers, not less.
What "Double-Keyed" Actually Means
Browsers traditionally key client-side state by the origin, or sometimes the registrable domain, of the location a resource was loaded from. MDN gives the canonical example: cookies, localStorage objects and caches available to an iframe loaded from https://example.com/hello.html are keyed by example.com. That holds whether the browser is loading example.com as the page you are on (first-party) or as an embed inside somebody else's page (third-party).
Trackers exploited exactly that. Put an example.com iframe or script on A.example and B.example, store a user identifier in its cookies or localStorage, and it reads the same identifier back on both sites. No collusion between the two sites is required; the shared bucket does the linking.
Partitioning adds a second key. The first key is still the origin of the resource being loaded. The second is the top-level site — in most cases the scheme plus the registrable domain of the page in your address bar. Here is what that produces when example.com is embedded in two sites:
| Where the code runs | Storage bucket it gets |
|---|---|
example.com visited directly | (example.com, example.com) |
example.com embedded in A.example | (example.com, A.example) |
example.com embedded in B.example | (example.com, B.example) |
That is three distinct buckets instead of one. The tracker still has storage — it simply cannot carry an identifier from the first bucket into the other two. Storing an ID while visited directly no longer lets it retrieve that ID while embedded elsewhere.
Note what did not change: on example.com itself, in a normal tab, everything works as before. Partitioning only bites when code runs in a third-party context.
What Gets Partitioned (It Is More Than Cookies)
The most common mistake is to treat this as "third-party cookie blocking, renamed." It is a different approach. Older policies block access to certain storage APIs in a third-party context; partitioning provides the embed a separate bucket for every top-level site, so nothing has to be blocked for the isolation to hold.
MDN's Firefox documentation splits the surface into three groups:
| Group | Examples | Behaviour in Firefox |
|---|---|---|
| Accessible storage | localStorage, sessionStorage, DOM Cache, IndexedDB, Broadcast Channel, Shared Workers, Service Workers | Partitioned by top-level site |
| Cookies | Third-party cookies | Partitioned by default, with a way to regain unpartitioned access (see below) |
| Network state | HTTP cache, image cache, favicon cache, connection pooling, DNS, HSTS, TLS session identifiers, OCSP, fonts | Permanently partitioned; sites cannot relax it |
The network row matters for privacy because these mechanisms were never meant to store data, yet can be abused as storage. That is the exact trick behind ETag and cache supercookies: a cached response that one site can set and another can read back is a tracking channel. Partitioning the HTTP cache by top-level site closes the cross-site version of it.
Where Each Browser Engine Stands
The honest answer is that there is no single "web platform" state, because the engines arrived at partitioning separately and on different schedules. What follows is limited to what the primary sources say; treat any version number as something to re-check against current documentation.
- Safari (WebKit) moved first. Its Intelligent Tracking Prevention, introduced in 2017, detects domains able to track a user across sites and either partitions their cookies or purges their website data. WebKit's own 2018 announcement of the Storage Access API describes ITP giving embedded content "only partitioned cookies" once it is classified as a cross-site tracker. It also notes that WebKit's initial implementation of the API covered cookies only and did not change how IndexedDB or
localStoragewere partitioned. - Firefox got there with what it calls State Partitioning (marketed as Total Cookie Protection). MDN records the rollout: network partitioning on by default for all users since Firefox 85, and dynamic partitioning — the cookie side — on by default since Firefox 103, after earlier opt-in stages for Strict mode (Firefox 86) and private browsing (Firefox 90).
- Chromium took a more piecemeal route. Rather than one switch, it partitioned pieces separately — its HTTP cache and, later, third-party storage APIs — and paired them with an opt-in partitioned-cookie mechanism, CHIPS, described below. Exact behaviour and version thresholds differ from Firefox's, so check the browser-compatibility data for the API you depend on rather than assuming parity.
The practical consequence for a developer: code that works in one engine because it relies on shared third-party state may quietly fail in another. The practical consequence for a user is that the same tracker faces different obstacles depending on which browser you run.
Partitioned Cookies: CHIPS and the Partitioned Attribute
Blocking third-party cookies outright breaks legitimate embeds — a chat widget or map that remembers a preference per site has no tracking motive, but a blanket block kills it. CHIPS (Cookies Having Independent Partitioned State) is the compromise: a site opts a cookie into partitioning with the Partitioned attribute, and the browser stores it under the double key.
Set-Cookie: __Host-widget=abc123; Secure; Path=/; SameSite=None; Partitioned
A cookie set this way is sent back only when the embed is loaded under the same top-level site that was present when it was set. MDN's Storage Access API page puts the trade-off plainly: because there is no privacy risk in a cookie that cannot follow you across sites, browsers send partitioned cookies in requests and make them available to embedded resources — but because the cookies are not shared between sites, they are also not automatically synchronized across sites.
That last clause is the cost of the model. A "log in once, be recognised everywhere" embed, such as a single sign-on frame, needs something stronger.
The Escape Hatch: the Storage Access API
The Storage Access API lets a cross-site iframe ask for its normal first-party cookies back. It was introduced in WebKit in 2018 — its announcement gives the origin story: developer feedback on ITP was that embedded content needed a way to authenticate users already logged in to their first-party services.
The flow has two methods:
document.hasStorageAccess()— a promise that resolves to whether the frame already has unpartitioned access.document.requestStorageAccess()— asks for access; must be called during a user gesture (MDN calls this transient activation), such as a click on a "Sign in" button inside the frame.
What the grant does and does not mean:
- It is per pair, not global. MDN describes the permission being stored with the structure
<top-level site, embedded site>. Access granted toexample.comembedded inembedder.comdoes not carry toexample.comembedded anywhere else. - It restores first-party cookies, not the embedder's data. WebKit's post is explicit that storage access "does not relax the same-origin policy in any way" — it is not the third party reaching into the host page's storage, or the reverse.
- Prompting differs by browser. Per MDN, Safari and Chrome prompt for embeds that have not previously received access, while Firefox prompts only once an origin has requested access on more than a threshold number of sites. In Chrome, embeds and embedders in the same related website set can skip the prompt.
- Firefox also grants access heuristically. To avoid breaking common integrations, it can grant an embed 30-day access after a user interacts with a popup it opened, or after a quick navigate-interact-return sequence. MDN labels these transitional and warns developers not to rely on them.
The design goal is that regaining access is a visible, per-site decision made in the context of a real interaction — not a switch a tracker can flip silently for every site at once.
What Partitioning Does Not Stop
It helps to be exact about the boundary, because "partitioned" is easy to over-read as "private."
- First-party tracking is untouched. A site can still recognise its own returning visitors with its own cookies. Partitioning targets cross-site linking only.
- It does not affect other cross-site tricks. Techniques that link visits by other means — for instance bounce tracking, which navigates you briefly through a tracker's own site so that it runs as a first party — sit outside the double key. So does detecting which sites you are logged in to.
- It is not user-chosen isolation. Firefox containers separate your own identities from one another; partitioning separates each site's embeds from one another. Firefox applies both at once, and neither substitutes for the other — see container tabs and tracking.
- It is not incognito mode. Private windows discard state when closed; partitioning applies in ordinary windows too. For how sites nonetheless try to notice private mode, see incognito detection.
- It is not a defence against a persistent ID. For identifiers designed to survive clears — the subject of persistent visitor IDs — partitioning removes one storage route, not the goal.
Why This Pushed Trackers Toward Fingerprinting
Here is the trade-off, stated plainly. Partitioning breaks state-based cross-site tracking: an identifier a tracker writes to storage on one site can no longer be read from another. Every technique in that family depended on the browser keeping one shared bucket per origin, and that assumption is gone.
A fingerprint does not use that route. It is computed from what the browser and hardware already reveal — canvas and WebGL output, fonts, screen and hardware values, audio behaviour — with nothing written to your device. No storage is involved, so there is nothing for the browser to partition, purge or block. The ID is not saved; it is recognised again on the next visit from the same inputs.
That is why closing the storage route raised the value of the fingerprint route for anyone who still wants cross-site linking. If you want to see which signals your own browser exposes, our browser fingerprinting guide covers the mechanics, and the fingerprint check shows a live readout.
Frequently Asked Questions
Is storage partitioning the same as blocking third-party cookies?
No. Blocking denies an embed access to storage in a third-party context. Partitioning gives it storage — a separate bucket per top-level site — so embeds keep working, but nothing they store on one site is visible on another.
Does partitioning break embedded logins and widgets?
It can. An embed that relied on one shared cookie across many sites to recognise a logged-in user finds a fresh, empty bucket on each site. The supported fixes are requestStorageAccess() from the Storage Access API (needs a user gesture) or, for per-site state, a cookie with the Partitioned attribute.
Does partitioning stop a site from tracking me on its own pages?
No. A first-party cookie is keyed by the site you are on, so partitioning leaves it alone. It removes the ability to link your activity between unrelated sites through shared embedded storage.
Do all browsers partition storage the same way?
No. Safari, Firefox and Chromium introduced partitioning at different times and with different scope, and details such as which storage APIs are covered and when a prompt appears differ. Check the current compatibility data for the exact API before relying on either behaviour.
Conclusion
Storage partitioning is one small change with a large effect: adding the top-level site as a second key means an embedded tracker's storage on one site is simply a different bucket from its storage on another. That removes the shared-state shortcut that cheap cross-site tracking relied on, while leaving a controlled way back — the Storage Access API, or the opt-in Partitioned cookie — for the legitimate cases that need it. It does not remove tracking; it moves the contest to places that need no storage at all, which is why understanding fingerprints matters more, not less, once your browser partitions state.
Recommended Reading:


