Platform in Storage
Safari's 7-day cap on script-writable storage
Published Updated
Intelligent Tracking Prevention deletes all of a website’s script-writable storage after seven days of Safari use without user interaction on the site. The clock counts days of Safari use, not calendar days. A web application added to the Home Screen runs outside Safari with its own counter, so the same site can lose data in a tab and keep it in the installed app.
How it works
Section titled “How it works”The cap arrived in two steps, and the second one is the one that reaches a PWA’s offline data.
What the seven days count
Section titled “What the seven days count”ITP 2.1, announced on 21 February 2019 with the beta releases of iOS 12.2 and Safari 12.1, capped all persistent client-side cookies, meaning persistent cookies created through document.cookie, to a seven-day expiry. WebKit’s stated reason was that trackers had started writing identifiers into the first party’s own cookie jar, where tracker scripts operating in the first-party context can read and write each other’s data.
The post of 24 March 2020, covering ITP in iOS and iPadOS 13.4 and Safari 13.1 on macOS, extended the same limit to the rest of script-writable storage, because third-party scripts had moved to first-party storage such as LocalStorage, which has no expiry function at all. Its wording of the trigger is exact: deleting all of a website’s script-writable storage after seven days of Safari use without user interaction on the site. Days of Safari use do not advance while the browser is unused, and interaction with the site resets the count.
Which storage the cap deletes
Section titled “Which storage the cap deletes”WebKit enumerates the affected script-writable storage forms, excluding some legacy website data types:
| Storage form | What a PWA loses |
|---|---|
| Indexed DB | Structured application data, including offline queues and synced records. |
| LocalStorage | Preferences, feature flags, and session hints kept in the synchronous store. |
| SessionStorage | Per-tab state for the deleted origin. |
| Media keys | Stored keys for encrypted media. |
| Service Worker registrations and cache | The registration itself along with its Cache Storage contents. The next visit has no existing controller; if the page calls register() again and creates a new registration, the new worker enters installation. |
This deletion can break offline use. An app with its shell in Cache Storage and its records in IndexedDB loses both in the same sweep, along with its service worker registration.
Why Home Screen web apps are counted separately
Section titled “Why Home Screen web apps are counted separately”WebKit addressed installed apps in the same 2020 post. The seven-day cap is gated on seven days of Safari use without user interaction on the site, and that is the case in Safari; web applications added to the Home Screen are not part of Safari and thus have their own counter of days of use, which matches actual use of the web application and resets the timer. WebKit states that it does not expect the first party in such a web application to have its website data deleted, and asks developers to report it as a serious bug if that happens, because deleting first-party data in web applications is not the intention of Intelligent Tracking Prevention.
Platform coverage
Section titled “Platform coverage”The cap is one eviction path among several. WebKit’s 2023 storage policy post lists three conditions under which data is deleted without the user or the website asking: exceeding the overall quota, system storage pressure, and the site not having been interacted with by the user for some time, which is the ITP path described above. The same post states that an origin may be excluded from eviction if it has an active page at the time of eviction or its storage is in persistent mode, and that by default all origins use best-effort mode.
Persistent mode is reachable from script. The post states that an origin can check the mode with StorageManager.persisted() and request a change with StorageManager.persist(), that the Storage API is fully supported starting in Safari 17.0 and in WebKit apps for iOS 17, iPadOS 17, and macOS Sonoma, and that WebKit grants the request based on heuristics like whether the website is opened as a Home Screen Web App. That post also notes that a standalone web app, whether a Home Screen Web App on iOS or added to the dock on macOS, gets the same origin quota and overall quota as the same site opened in a browser app, and that cookies and the HTTP cache are not bound by the quota policy that governs the storage APIs.
Examples
Section titled “Examples”The examples recover when a Service Worker registration is missing and request the storage mode that WebKit says may exclude an origin from eviction.
Recovering when a service worker registration is missing
Section titled “Recovering when a service worker registration is missing”WebKit says the seven-day deletion includes Service Worker registrations and cache. A missing registration does not establish why it is absent, so this example treats that state as a reason to register again and fetch bootstrap data, not as proof that the ITP deletion ran.
async function reseedFromServer() { const response = await fetch('/api/bootstrap'); localStorage.setItem('bootstrap', await response.text());}
async function restoreMissingRegistration() { if (!('serviceWorker' in navigator)) { return 'unsupported'; } const registration = await navigator.serviceWorker.getRegistration(); if (!registration) { await navigator.serviceWorker.register('/sw.js'); await reseedFromServer(); return 'missing-registration-reseeded'; } return 'intact';}
restoreMissingRegistration().then((state) => console.log('storage state:', state));The branch handles the documented deletion mechanism without treating registration absence as evidence of its cause. WebKit’s posts describe deletion of client-side website data, not deletion of a server-side account.
Asking for persistent mode from the installed app
Section titled “Asking for persistent mode from the installed app”Request persistent mode after installation: WebKit’s heuristics favour a site opened as a Home Screen Web App over a first visit in a tab. Check the result returned by the function; a request can be denied.
async function ensurePersistentStorage() { if (!('storage' in navigator) || !navigator.storage.persist) { return false; // no Storage API: plan for deletion } if (await navigator.storage.persisted()) { return true; } return navigator.storage.persist(); // may resolve false; handle both}A false result means the origin stays in best-effort mode. Its data must be reconstructible from the server. A true result is not an exemption. The 2023 post’s wording is that an origin may be excluded from eviction if its storage is in persistent mode, which is a condition rather than a guarantee, and it describes the storage policy rather than promising anything about the seven-day sweep, so the recovery path from the previous example is worth keeping either way.
Correction (2026-10-20)
Section titled “Correction (2026-10-20)”This revision limits the first-party cookie statement to tracker scripts, stops treating a missing Service Worker registration as proof of an ITP deletion, and removes the unsupported Settings-based test procedure and account-loss outcome.
See also
Section titled “See also”- Full Third-Party Cookie Blocking and More (webkit.org)
- Intelligent Tracking Prevention 2.1 (webkit.org)
- Updates to Storage Policy (webkit.org)
- Service Workers:
register()(W3C) - Eviction and best-effort storage
- StorageManager.persist() and persisted()
- iOS Add to Home Screen
Specifications
| Specification | Status |
|---|---|
| None. | |