Skip to content

Back to projects

PWA

A cache that only busts itself when something actually changes

· 6 min

A site running as a PWA had a service worker handling two jobs: serving the application shell (HTML, CSS, JS, icons) straight from cache before even trying the network, and keeping JSON data fresh in the background without blocking the response. It works well, until the day the site changes and the cache needs to keep up with that change.

Illustration of a block pattern, like a fingerprint

The naive way: a number someone has to remember to change

The most common way to do this is naming the cache with a hand-written version string, bumped on every deploy. The risk hides in plain sight: if anyone forgets to bump that number on a change, the deploy ships new CSS and JS, but every user's service worker still recognizes the old cache as valid and keeps serving the exact same files as before, indefinitely. Nothing breaks visibly, the site just looks like it "didn't update", and the symptom only shows up well after the deploy, far from any log that would point at the cause.

Taking human memory out of the equation

The fix was to stop typing that number by hand. The cache version identifier is now computed at build time: a SHA-256 hash of the final CSS content, the already-bundled JS, and the icon sprite, truncated to a short form. Any real change to those files changes the hash, which changes the cache name, which makes the service worker's activation step recognize the old cache as stale and delete it, with nobody needing to remember to do anything.

Two strategies, one shared foundation

The application shell uses cache-first with an offline-page fallback; JSON data uses stale-while-revalidate, serving the cached version immediately while fetching the fresh one in the background. The two strategies are different, but both depend entirely on the cache name being trustworthy, fixing the versioning fixed the foundation both were built on.

The result

Serving a stale version after a deploy stopped being a risk that depended on someone remembering a manual step, it became structurally impossible, because there's no longer a number left to forget to update.