Diagnosing Safari ITP Cookie and Storage Failures: A Practical Guide for Authentication and Analytics
A diagnostic guide for Safari Intelligent Tracking Prevention (ITP) cookie and storage restrictions that break authentication, analytics, and cross-site workflows on macOS/iOS Safari 16+. Includes recognizable symptoms, cause table, ordered checks, fixes tied to findings, and escalation criteria.
18 Jul 2026, 07:16 UTC

Recognizable Condition
Users on Safari 16+ (macOS Ventura, iOS 16, and later) report being logged out after roughly 24 hours, third-party authentication redirects (OIDC, SAML, social login) failing silently, or analytics cookies vanishing even though the server sets SameSite=None; Secure. The symptoms appear only in Safari; Chrome and Firefox behave as expected.
Cause and Diagnostic Quick-Reference
| ITP Version | Behavior | Observable Symptom |
|---|---|---|
| ITP 2.1+ (Safari 12.1+) | Partitions all third-party storage (cookies, localStorage, IndexedDB) per top-level eTLD+1 | Embedded iframes or subresource requests lose access to cookies set on the third-party domain |
| ITP 2.2+ (Safari 12.2+) | Deletes all script-writable storage (document.cookie, localStorage, sessionStorage, IndexedDB) after 7 days of no user interaction with the embedding site | Returning users find localStorage cleared, session tokens gone, analytics identifiers reset |
| ITP 2.3+ (Safari 13.1+) | Caps client-side document.cookie writes to a 7-day expiry if no user gesture preceded the write | Cookies set via JavaScript expire in 7 days regardless of server-set Max-Age |
| ITP 2.3+ Link Decoration | Strips query parameters identified as cross-site tracking decoration on navigation from classified trackers | Attribution tokens (e.g., ?gclid=, ?fbclid=) disappear on landing page |
Ordered Diagnostic Checks
1. Inspect Cookie Attributes in Web Inspector
Where: Safari → Develop → Show Web Inspector (or ⌥⌘I) → Storage tab → Cookies.
What to verify:
Expires/Max-Agevalues — are they capped at 7 days?Partitionedattribute (CHIPS) — present on cross-site cookies?SameSitevalue —NonewithSecurefor cross-site;LaxorStrictfor same-site.
Risk: None; read-only inspection.
2. Check Console for ITP Warnings
Where: Web Inspector → Console tab.
Look for: Messages like Storage Access API access was denied, Cookie "name" will be soon rejected, or ITP prefixed warnings.
Permission: Requires Develop menu enabled (Safari → Settings → Advanced → Show features for web developers).
3. Confirm User Gesture Precedes Storage Write
ITP 2.3+ only grants full cookie lifetime when a transient user activation (click, tap, key press) occurs within the same task. Programmatic writes (e.g., setTimeout, fetch().then()) without a gesture receive the 7-day cap.
Test: Add a breakpoint before document.cookie = ... and verify window.event?.isTrusted === true.
4. Test Storage Access API Flow
Where: Console in the embedded (third-party) context.
Command:
document.hasStorageAccess().then(has => { console.log('hasStorageAccess:', has); if (!has) return document.requestStorageAccess();}).then(() => console.log('Storage access granted')).catch(err => console.error('Denied:', err));Expected: A user gesture (click) must precede requestStorageAccess(); otherwise the promise rejects with NotAllowedError.
Limitation: The prompt appears at most once per session per embedded origin.
5. Validate Server-Sent Cookie Headers
Where: Network tab → select auth/analytics response → Headers → Set-Cookie.
Required for cross-site:
Set-Cookie: session=abc123; SameSite=None; Secure; Partitioned; Path=/; Max-Age=2592000Partitioned (CHIPS) opts the cookie into per-top-level-site storage, bypassing ITP partitioning. Requires Safari 16.4+; older versions ignore the attribute silently.
Fixes Tied to Findings
For ITP 2.3+ Client-Side Cookie Cap (7-Day Limit)
Fix: Move authentication cookies to HTTP-only, server-set cookies. The backend sets Set-Cookie on login responses; JavaScript never writes them.
Implementation: Remove document.cookie writes for session identifiers. Use a short-lived access token in memory (or sessionStorage for tab-scoped access) and a long-lived HTTP-only refresh token cookie.
Verification: After login, confirm document.cookie returns empty for the session cookie; Web Inspector shows it with HttpOnly flag.
For ITP 2.1+ Third-Party Partitioning
Option A — CHIPS (Partitioned Cookies): Add Partitioned to cross-site Set-Cookie headers. The cookie is keyed by (top-level eTLD+1, cookie domain, name), so each embedding site gets its own jar.
Option B — Storage Access API: In the embedded iframe, call document.requestStorageAccess() after a user click. On success, the iframe gains unpartitioned access to its cookies.
Decision: Use CHIPS for analytics/ads where silent access is needed. Use Storage Access API for authentication flows where a user gesture ("Continue with…" button) naturally exists.
For ITP 2.2+ 7-Day Script-Writable Storage Purge
Fix: Implement silent re-authentication via backend refresh-token rotation.
Flow:
- Access token (JWT, 15 min) stored in memory.
- Refresh token stored in HTTP-only,
Partitionedcookie (or same-site if auth domain matches top-level). - On 401, frontend calls
/auth/refresh(credentials: 'include'). Backend rotates refresh token, sets new HTTP-only cookie. - If refresh fails (cookie purged), redirect to login — user perceives a single session.
Risk: Refresh endpoint must be same-site or CHIPS-partitioned; otherwise ITP blocks the cookie on the refresh request.
For Link Decoration Stripping
Fix: Replace query-parameter attribution with Private Click Measurement (PCM) or server-side click IDs stored in first-party cookies.
PCM Example: Ad click registers .well-known/private-click-measurement/ endpoint; conversion fires attribution-reporting headers. No client-side decoration survives ITP.
Escalation Criteria
- File a WebKit bug (bugs.webkit.org) if:
document.requestStorageAccess()never shows a prompt despite a verified user gesture (click handler,event.isTrusted === true).Partitionedcookies are rejected on verified same-site requests (same eTLD+1).
- Engage Apple Developer Support (requires paid membership) when:
- Enterprise MDM profiles enforce cookie blocking or disable ITP, causing inconsistent behavior across managed vs. unmanaged devices.
- App Store review rejects an app for "ITP non-compliance" without clear guidance.
Platform-Specific Cautions
- macOS vs. iOS minor versions: ITP logic can diverge (e.g., iOS 17.4 tightened link decoration rules). Test on both physical devices.
- Private Browsing: Applies stricter ephemeral limits (storage wiped on window close) unrelated to ITP. Diagnose in normal windows first.
- MDM Overrides: Managed devices may have
com.apple.Safari.ContentBlockersor cookie policies that supersede ITP. Confirm with IT before deep debugging. - CHIPS Support: Safari 16.4+ only. Older versions ignore
Partitioned— cookies fall back to partitioned-by-default behavior. - Storage Access API Gesture Requirement: Programmatic calls without a transient user activation are denied. The gesture must be in the same call stack (not a
setTimeoutcallback).
Verification Checklist
- Open Safari → Develop → Web Inspector → Storage → Cookies; confirm
Partitionedflag andExpiresvalues. - In Console, run
document.cookieafter 7 days of no site interaction (or simulate by advancing system clock + clearingLastVisitedtimestamps in~/Library/Safari/— advanced, backup first). Observe truncation. - Test Storage Access API:
document.hasStorageAccess().then(has => has || document.requestStorageAccess())after a real click. - Compare normal vs. Private Browsing windows for storage persistence differences.
- Check WebKit ITP changelog for the exact ITP version shipping with your target Safari release.
Limitations
This guide covers ITP 2.1–2.3+ behaviors as of Safari 16–17. Apple does not publish a formal specification; behavior is inferred from WebKit source, blog posts, and empirical testing. Future releases may change thresholds (e.g., 7-day → 1-day) or add new classifications. Always verify against the current WebKit blog and test on target OS versions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.