Pain Tracker, an open-source pain logging PWA, offers a lesson in deterministic service worker behavior: never cache navigation requests.
The repo's service worker uses network-first for all HTML navigations, falling back to a cached offline.html only when the network fails. This avoids the most common PWA failure: a cached index.html pointing to build artifacts that no longer exist.
The pattern matters because modern bundlers (Vite, webpack, Rollup) generate content-hashed chunk filenames on each build. If a service worker caches the HTML with cache-first strategy, users end up with old HTML requesting chunk-abc123.js while the server only has chunk-def456.js. Result: 404s, blank screens, and support tickets.
Pain Tracker takes a conservative approach to caching. Static assets (scripts, styles, images, fonts) use cache-first with explicit allowlists for same-origin GET requests matching /assets/, /icons/, and specific file extensions. API responses are never cached by the service worker.
Versioned cache names (pain-tracker-static-v1, v2, etc.) provide deterministic cleanup. On activation, the worker deletes all caches with the prefix that don't match the current version. No orphaned caches, no guessing about state.
The repo includes two service workers: one for root deployment, one for GitHub Pages at /pain-tracker/ base path. The duplication prevents the classic scope mismatch that breaks offline behavior in different deployment contexts.
The trade-off is explicit: faster first-load for returning users, guaranteed fresh HTML on every navigation, predictable behavior after deploys. For health-adjacent applications where user trust depends on consistent behavior, the conservative approach makes sense.
Enterprise teams shipping PWAs can adopt this pattern: network-first navigations, cache-first static assets, versioned caches, explicit base path handling. The boring approach that prevents midnight debugging sessions.
The code is public: github.com/CrisisCore-Systems/pain-tracker