When a device runs low on disk space, how does a browser evict a best-effort origin's stored data, and what does that granularity mean for an offline-capable web app?
answer
- the disk, not your origin, triggers it
- least recently used goes first
- not row-by-row
- everything the origin holds, together
- looks exactly like a first visit
basics
~20 sBrowsers evict a whole best-effort origin at once, least-recently-used first: its databases, caches and web storage all go together, never just the oldest records inside one store. Offline apps must therefore treat client storage as a cache that can be empty on the next visit.
solid answer
~50 sEviction is triggered by the device, not by your origin: when free disk falls below the browser's threshold, it reclaims space from best-effort origins, typically picking the least recently used one. The part people get wrong is the granularity. The browser does not prune your oldest rows or your largest cache — it deletes everything that storage key holds, so IndexedDB databases, `CacheStorage` entries, the origin private file system and web storage vanish together. Your app can be at 1% of its quota and still lose the lot. That makes the only safe design a defensive one: assume the store may be empty on the next visit, detect emptiness at startup and re-hydrate from the server, and never leave unsynced user work sitting locally as the sole copy. Requesting persistence exempts you from this path, but not from the user clearing site data.
go deeper
Know that client-side data is not permanent: the browser can delete it when the device is short of space, so an app must cope with finding its storage empty.
Be ready to explain that eviction operates on the whole storage key at once, that selection is roughly least-recently-used, and that being well under quota is no protection.
Demonstrate the design response: a startup emptiness check that re-hydrates, no unsynced work held as the sole copy, self-pruning of stale caches, and instrumentation that makes silent eviction visible in production data.
Own the offline data contract across the product: which data may be client-only, what the recovery cost is when it disappears, and how you keep local-first features from quietly losing user work at scale.
## What triggers eviction Quota-managed storage lives on the user's disk, and the browser is the one accountable for that disk staying usable. When free space drops below the browser's internal threshold — or when the total across all sites crosses a global limit — the browser starts reclaiming. Critically, the trigger has nothing to do with your origin. You can be far under your own quota and still be evicted, because the pressure came from the operating system, from another site, or from the user filling the disk with video. Any mental model of the form "we only store 20 MB, we are fine" is wrong. The selection policy in practice is least-recently-used across best-effort origins: the site the user has not visited for the longest goes first. An app used daily is comparatively safe; an app used quarterly is a prime candidate. ## The unit of eviction is the whole origin This is the crux of the question, and the point interviewers are usually probing. Eviction is **all-or-nothing per storage key**. The browser does not open your database and delete old rows, and it does not trim the largest cache and leave the rest. It removes everything that storage key owns: - every IndexedDB database - every `CacheStorage` cache - files under the origin private file system - `localStorage` and `sessionStorage` - service worker registrations for the origin So the failure mode is not "some data is missing", which is easy to detect halfway through a read. It is "the app looks like a first-ever visit" — including the offline shell you cached, including the flag you set to record that the user finished onboarding. That matters for the code you write. A defensive read that patches over a missing record is the wrong shape. The right shape is a startup check that asks whether the store exists at all and re-establishes it from scratch. ## The other ways data disappears Disk-pressure eviction is the involuntary path, but it is not the only one, and a senior answer names the rest. **The user.** Clearing browsing data, clearing site data for a single site, or removing a site in browser settings deletes everything — persistent storage included. **Your own server.** A response carrying `Clear-Site-Data: "storage"` instructs the browser to drop the origin's script-writable storage; `"cache"`, `"cookies"` and `"*"` cover the other buckets. This is a deliberate tool, most often used on logout so a shared machine is not left holding one user's cached data. **Tracking protection.** Safari's rules cap the lifetime of script-writable storage for sites the user does not interact with, deleting it after roughly a week of no interaction. An app that is genuinely used occasionally can therefore find itself empty on Safari without any disk pressure at all. **Private browsing.** Storage in a private or incognito session is small and discarded when the session ends. ## Designing for it The governing principle is that client storage is a cache, and the server holds the truth. Everything else follows. **Detect emptiness explicitly at startup.** Do not infer state from the presence of individual keys. Check for the store, then re-hydrate. ```js async function bootstrap(db) { const version = await db.get('meta', 'schemaVersion'); if (version === undefined) { await hydrateFromServer(db); // treat as a first run } } ``` **Do not let unsynced user work be the only copy.** If someone can compose something offline, the sync queue is the most valuable thing on the device and the most painful to lose. Flush it as soon as connectivity allows, and consider requesting persistence specifically because of it. **Make cached data reconstructible.** Anything derived — search indexes, thumbnails, precomputed views — should be rebuildable from a server fetch rather than treated as authoritative. **Ask for persistence when the data justifies it.** `navigator.storage.persist()` removes the disk-pressure path, and `navigator.storage.persisted()` tells you where you stand. It does not remove any of the other deletion paths. **Prune your own data before the browser prunes it for you.** An origin that keeps its footprint modest is a less attractive eviction target and is less likely to hit its own quota; delete stale caches and expired records on a schedule rather than accumulating forever. ## Instrumentation Because eviction is silent, teams often do not know it is happening. A cheap signal is to record a durable marker server-side when a client first initialises its store, then report at startup whether the client believed it was a first run. A population of returning users reporting first-run initialisation is eviction, or one of its siblings, showing up in the data — and it tells you whether the offline experience is actually holding.
- How does an app detect that it was evicted, given the browser sends no notification?By treating a missing store as a signal rather than an anomaly. Check at startup for a marker record you always write on initialisation; if it is absent while the session or account says the user is a returning one, the store was wiped. Reporting that mismatch to the server turns a silent platform behaviour into a metric you can watch.
- Besides disk pressure, what else deletes an origin's client storage?The user clearing browsing or site data; a Clear-Site-Data response header from your own server, most often on logout; tracking-protection rules such as WebKit's cap on script-writable storage for sites the user has not interacted with for about a week; and the end of a private-browsing session, whose storage is discarded outright.
- Does keeping the app's footprint small protect it from eviction?It helps but does not protect. Selection is driven mainly by recency of use, not size, so an infrequently visited origin is evicted even though it stores very little. A small footprint reduces the chance of hitting your own quota and makes re-hydration cheaper, but only persistence removes the automatic-eviction path.
saying these in an interview costs you the question
- Believes the browser deletes the oldest records first
- Thinks staying under quota prevents eviction entirely
- Expects an event or callback when data is evicted
- Assumes Cache API entries go but IndexedDB survives
- Stores unsynced user work locally as the only copy