Updates to Storage Policy

A website can store data on a user’s device using various storage APIs: localStorage, IndexedDB, File System, etc. It’s an important capability allowing websites to be functional offline with good performance by serving local resources. However, the storage capacity is not unlimited. In fact, it is usually much smaller than a device’s disk size. Bad things could happen when the capacity is full like failed storage operations or data eviction. As a web developer, understanding storage policy helps to avoid these unexpected results. This post will discuss the latest storage policy in WebKit apps including Safari.

Scope

There are many types of website data, and the policy discussed in this post is mostly related to the types created by storage APIs: localStorage, Cache API, IndexedDB, Service Worker, and File System. Other types like cookies and HTTP cache are currently not subject to the policy below — for example, they are not bounded by quota.

Storage Quota

Storage quota defines how much data can be stored. There are two types of quota in WebKit: origin quota and overall quota.

Origin quota

The origin quota is the storage limit of one origin. If the limit is reached, the storage operation that requires space will fail, and a QuotaExceededError exception will be thrown. Starting in Safari 17.0, and in WebKit apps for iOS 17, iPadOS 17 and macOS Sonoma:

  • For a browser app, the origin quota is up to 60% of the total disk space.
  • For other apps, the origin quota is up to 15% of the total disk space.

An app is a browser app if it can be set as default browser (see Apple Developer documentation). With this change, Safari 17.0 no longer prompts users about a website wanting to use more space.

A cross-origin frame uses a different storage partition than the frame it is embedded in to prevent tracking, and thus it has a different quota. The quota is currently 10% of the main frame’s origin quota.

Overall quota

The overall quota is the storage limit of all origins. Reaching the limit can lead to data eviction, which releases storage used by the app. Starting in Safari 17.0, and in WebKit apps for iOS 17, iPadOS 17 and macOS Sonoma:

  • For a browser app, overall quota is up to 80% of the total disk space.
  • For other apps, overall quota is up to 20% of the total disk space.

When a web app is running standalone (as Home Screen Web App on iOS or Web App added to dock on macOS), it has the same origin quota and overall quota as when it is opened in a browser app.

Storage Eviction

Eviction means automatic website data deletion that is not initiated by the user or website. It can happen under a few conditions: when exceeding the overall quota, when the system is under storage pressure, or when the site has not been interacted with by the user for some time (see Intelligent Tracking Prevention).

WebKit normally evicts data on an origin basis: the data of an origin will be deleted as a whole. The ordering of origins to be deleted is decided using a least-recently-used policy. The last use time is the time of the last user interaction, or the time of the last storage operation.

Origin might be excluded from eviction if it has active page at the time of eviction, or its storage is in persistent mode. By default, all origins use a best-effort mode, which means their persistence is not guaranteed and their data can be evicted. An origin can request persistent mode using the Storage API introduced below.

Storage API

Different browsers may have different storage policies, and the Storage API provides a standard way for a website to get information about the current storage policy. Starting in Safari 17.0, and in WebKit apps for iOS 17, iPadOS 17 and macOS Sonoma, the Storage API is fully supported.

An origin can get estimated usage and quota values with StorageManager.estimate(). Usage indicates how much space is already used, and the quota is the origin quota.

if (navigator.storage && navigator.storage.estimate) {
    const storageEstimate = await navigator.storage.estimate();
    const availableSpace = storageEstimate.quota > storageEstimate.usage ? storageEstimate.quota - storageEstimate.usage : 0;
}

Note that the quota is an upper limit of how much can be stored — there is no guarantee that a site can store that much, so error handling for QuotaExceededError is necessary. Also, to reduce fingerprinting risk introduced by exposing usage and quota, quota might change based on factors like existing usage and site visit frequency.

An origin can check whether storage is in persistent mode with StorageManager.persisted() and request to change the mode to be persistent with StorageManager.persist(). WebKit currently grants a request based on heuristics like whether the website is opened as a Home Screen Web App.

if (navgator.storage && navigator.storage.persisted) {
    const persistent = await navigator.storage.persisted();
    if (!persistent && navigator.storage.persist)
        const result = await navigator.storage.persist();
}

If your website or web app uses storage APIs and expects to store a lot, you should adopt the Storage API to make most out of limited space. If you have any feedback or encounter an issue, please file an issue on bugs.webkit.org under component “Website Storage”.