OpenPWAStore
Back to News
Guide · May 19, 2026

Cache storage eviction rules determine whether your PWA survives storage pressure

Your PWA's cached content can be evicted. Here's how browsers decide, and how to make your app resilient.

OpenPWA Editorial4 min read
Cache storage eviction rules determine whether your PWA survives storage pressure cover

Why This Matters

Browsers maintain separate storage budgets and eviction policies. When storage pressure hits—low disk space, user action, periodic cleanup—your PWA's cached assets can be evicted without warning. If your app requires cached content to work offline and that content gets evicted silently, your offline functionality breaks. Users will blame your app, not understand that browser storage policies executed.

The Cache Storage Layer

Cache Storage is where your service worker stores responses:

const cache = await caches.open('v1');
await cache.addAll([
  '/',
  '/styles.css',
  '/scripts.js',
  '/images/logo.png'
]);

This resides in browser storage space alongside IndexedDB, Web Storage (localStorage/sessionStorage), and other APIs.

Browser Eviction Policies

Chrome:

  • Uses "eviction pressure" model
  • Least recently used (LRU) eviction within same-origin storage
  • Evicts entire origin if storage limit reached
  • Eviction threshold: roughly 50-60% of disk space across all browser origins

Firefox:

  • Per-origin eviction with LRU logic
  • More aggressive on low disk space
  • Can evict caches even for active PWAs
  • User can manually clear site data from settings

Safari:

  • Known for aggressive cache eviction, especially on iOS
  • Periodic cleanup removes old caches automatically
  • iStorage gets evicted more aggressively than Cache Storage
  • TWA packages (for Android) have better persistence due to native packaging

The Storage Pressure Triggers

Browsers evict for these reasons:

  1. Low disk space - When the device has less than ~100MB free
  2. User clears site data - Manual action in browser settings
  3. Periodic maintenance - Safari and Firefox clean up old caches
  4. Storage quota exceeded - Your app tries to store more than allowed

You can't prevent triggers 1, 3, and 4. You can only design for resilience.

Detection and Estimation

Storage Manager API:

const estimate = await navigator.storage.estimate();
console.log(`Quota: ${estimate.quota} bytes`);
console.log(`Usage: ${estimate.usage} bytes`);
console.log(`Percent used: ${Math.round(estimate.usage / estimate.quota * 100)}%`);

Persistence grants:

navigator.storage.persist().then((persistent) => {
  if (persistent) {
    console.log('Storage persistence granted');
  } else {
    console.log('Persistence denied - data may be evicted');
  }
});

Even with persistence granted, eviction can still happen—it just makes it less likely.

Survival Strategies

Keep your cache lean:

  • Only cache what's essential for offline functionality
  • Use ephemeral caching (don't persist) for content that can be re-fetched
  • Implement cache versioning to remove old versions
// Delete old cache versions
const cacheNames = await caches.keys();
for (const name of cacheNames) {
  if (name !== currentCacheName) {
    await caches.delete(name);
  }
}

Detect and recover from eviction:

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then(async (cacheNames) => {
      // Check if our expected cache exists
      const hasCache = cacheNames.includes(currentCacheName);
      if (!hasCache) {
        // Cache was evicted, re-populate it
        return caches.open(currentCacheName).then((cache) => {
          return cache.addAll(essentialAssets);
        });
      }
    })
  );
});

Implement fallback to network:

When cached content is missing, fetch from network and update the cache:

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      if (response) {
        return response;
      }
      return fetch(event.request).then((response) => {
        // Cache the new response for next time
        return caches.open(currentCacheName).then((cache) => {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

The Eviction Rubric

Use this checklist to assess risk:

| Factor | Low Risk | Medium Risk | High Risk | |--------|----------|-------------|-----------| | Cache size | < 10MB | 10-50MB | > 50MB | | Content type | Static assets only | Mixed content | User-generated ephemeral data | | User engagement | Daily active | Weekly active | Monthly active | | Platform | Android (native packages) | Desktop | iOS Safari |

High risk + unclear fallback = broken offline experience for users.

Monitor Cache Health

Track these metrics in production:

  • Cache hit rate (how often fetches are served from cache)
  • Cache miss rate for essential assets
  • Storage quota percentage over time
  • Eviction detection (when expected caches disappear)
  • Re-cache success rates

Alert developers when:

  • Cache hit rate drops below 80%
  • Storage usage consistently exceeds 70% of quota
  • Eviction is detected for essential caches

For Developers

Cache eviction is inevitable. Design your PWA to handle it gracefully. Offline reliability isn't about preventing eviction—it's detecting it and recovering transparently.

Treat cache storage as volatile. Implement re-caching logic on service worker activation. Design pages to work when cached content is missing (show loading states, fetch on demand, provide fallback UI).

Next Steps

  1. Implement cache eviction detection in your install/activate handlers
  2. Add Storage Manager API telemetry to your error tracking
  3. Design cache-critical assets vs. cache-optional assets
  4. Test eviction scenarios by manually clearing cache and observing behavior
  5. Document your cache recovery plan for on-call engineers

Your PWA should survive cache eviction, not die from it.