Safari ITP and Partitioned Cookies: Debugging Third-Party Embeds Without Guesswork
Safari's ITP partitions third-party cookies by top-level site, breaking embeds that assume global storage. Learn how to diagnose partitioned cookies and use the Storage Access API to restore legitimate access.
19 Jul 2026, 17:17 UTC

You ship a third-party payment widget or single sign-on iframe. It works in Chrome and Firefox, but in Safari the session disappears, users are unexpectedly logged out, or your analytics dashboard shows every visit as a brand-new user. The code has not changed. The likely culprit is Safari's Intelligent Tracking Prevention (ITP), which isolates client-side storage by the top-level site rather than treating cookies as globally shared.
This is not a bug you can bypass with a clever cookie attribute. ITP is a storage isolation model. To keep legitimate cross-site embeds working, you either design them to tolerate partitioned storage or explicitly request unpartitioned access through the Storage Access API after a user gesture.
How ITP partitions storage
Starting with Safari 12 (ITP 2.0), Safari stores third-party cookies in partitioned containers keyed by the top-level site. A script from widget.example embedded in shop.example cannot read the same cookie it would read if embedded in news.example. The same partitioning logic applies to localStorage, IndexedDB, and service workers, closing common workarounds used for cross-site tracking.
Because ITP is enabled by default and is not exposed as a user-facing toggle in standard Safari settings, you cannot ask affected users to disable it. Your site must behave correctly under partitioned storage even if you primarily target other browsers.
The Storage Access API as the escape hatch
For legitimate embeds that genuinely need to share state across sites—such as an SSO login frame or a payment session—Safari provides the Storage Access API. The third-party document can call document.requestStorageAccess(), which returns a promise. If the user has interacted with the embedded content and consents, the browser grants temporary access to the unpartitioned cookie jar.
Two constraints matter here. First, the request must originate from inside the third-party frame, not the parent page. Second, it should be triggered by a user gesture, such as a click or tap, rather than automatically on page load. You can check the current state without prompting by calling document.hasStorageAccess().
Worked example: diagnosing a broken iframe session
Suppose an authentication iframe stops recognizing returning users in Safari. You can confirm ITP partitioning and test the Storage Access API using Safari's Web Inspector.
- Open the host page in Safari, then enable the Develop menu and choose Show Web Inspector (or press
Option+Command+I). - Select the Storage tab and inspect cookies for the third-party iframe origin. Under ITP, the cookie appears inside a partitioned container labeled with the top-level site, not as a globally shared entry.
- Switch to the Network tab and reload. Requests from the iframe that would normally carry a session cookie omit it, confirming the partition is active.
- Inside the iframe, wire a visible button to the Storage Access API and click it:
// Runs inside the third-party iframe document
async function ensureSessionAccess() {
if (await document.hasStorageAccess()) {
return true;
}
try {
await document.requestStorageAccess();
return true;
} catch (err) {
// The user declined or browser policy blocked the request
return false;
}
}
document.querySelector('#continue-button').addEventListener('click', async () => {
const granted = await ensureSessionAccess();
if (granted) {
// Retry the authenticated request now that cookies are readable
}
});
- After the click, watch the Network tab. The next iframe request should include the previously missing cookie. When the temporary access window expires, subsequent requests will omit it again, which confirms ITP's timeout behavior.
This flow gives you a concrete diagnostic decision: if the cookie is visible in the Storage tab but absent from Network requests, partitioning is the cause. If requestStorageAccess() rejects even after a gesture, the embed may be relying on a pattern ITP does not permit, and you need an architectural change rather than a retry loop.
Trade-offs and limitations
The Storage Access API solves the "broken embed" problem, but it introduces friction. The prompt interrupts the user, and access is temporary: research and browser documentation describe a limited window after the last interaction, historically on the order of 24 hours for third-party cookies under ITP 2.1 and later, though exact durations can change across Safari releases. Long-lived sessions cannot depend on a single grant.
Analytics pipelines face a different limitation. If your measurement relies on a third-party persistent identifier, ITP's partitioning and shortened cookie lifetime inflate new-user counts and fragment session data. There is no client-side fix for this; you need a first-party measurement strategy or server-side correlation.
| Scenario | Behavior under ITP | Practical response |
|---|---|---|
| Third-party SSO iframe | Session cookie partitioned; user appears logged out | Call requestStorageAccess() after a login click |
| Payment widget session | Cart or token state not shared across top-level sites | Pass tokens via URL or postMessage, not cookies |
| Cross-site analytics | Identifier reset or partitioned per site | Move to first-party identifiers or server-side logs |
What to do next
Audit every third-party embed on your site for hidden dependencies on cookies, localStorage, or IndexedDB. Where shared state is genuinely required, gate the flow behind a visible user action and request storage access explicitly. Where it is not, refactor the embed to receive tokens through the parent page or a redirect.
Finally, make Safari part of your standard test matrix. Open Web Inspector, load the page with the embed, and check the Storage and Network tabs before you assume the integration is healthy. Partitioned storage is the default, not an edge case.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.