PWA Guide

Offline caching for assets and API

What to cache, what never to cache, and how to version caches so users do not stick on an old app shell.

All materials

1. What to cache

TypeCache?Strategy
App shell (HTML/CSS/JS)YesPrecache + version bump
Fonts / iconsYesCache First
ImagesSelectivelyCache First with size limits
Public API listsSometimesNetwork First or SWR
Auth / payments / private dataNoNetwork Only

2. Versioning pattern

const VERSION = 'v3';
const STATIC_CACHE = `static-${VERSION}`;
const DATA_CACHE = `data-${VERSION}`;

// On activate: delete caches that do not match VERSION
self.addEventListener('activate', (event) => {
  event.waitUntil((async () => {
    const keys = await caches.keys();
    await Promise.all(
      keys
        .filter((key) => !key.endsWith(VERSION))
        .map((key) => caches.delete(key))
    );
  })());
});

Every release that changes shell files should bump VERSION.

3. Navigation offline fallback

async function handleNavigation(request) {
  try {
    return await fetch(request);
  } catch {
    return (await caches.match('/offline.html')) || Response.error();
  }
}

4. Avoid sticky old apps

  • Do not precache hashed assets under a forever cache name.
  • Use skipWaiting() + clients.claim() carefully; tell users when a refresh is needed.
  • For HTML, prefer Network First so content updates are visible.
  • Send a Clear-Site-Data only as an emergency escape hatch.

5. Safari notes

  • Storage quotas are tighter; cache only what you need.
  • Do not assume long-lived offline data for rarely opened PWAs.
  • Test “airplane mode” on a physical iPhone after install to Home Screen.