Service workers are a cool technology; they make it possible to build fully offline apps on the web by acting as a persistent cache. However, this also creates a tricky problem: cache invalidation.

If a cache is not refreshed often enough, users will be served old files. I experienced this at work a few months ago; pushing changes to an internal web app sometimes had no effect. A hard refresh would fix the issue, but it was easy to forget if the change was subtle. This issue appeared and disappeared fairly randomly. It wasn’t a big deal, but it did cause frustration whenever people tried to test newly added features.

Various fixes were tried, but they all either didn’t work or caused a worse problem (such as an infinite refresh loop). I was new to the codebase, but the service worker seemed pretty suspicious as it was one of the few cache-related parts of the app. The service worker had been created by create-react-app and didn’t look like it had been touched. I suggested we remove it just to be sure it wasn’t causing the issue….and our problems went away! Kind of!

// Code from Chris Love
// https://love2dev.com/blog/how-to-uninstall-a-service-worker/
navigator.serviceWorker.getRegistrations().then(registrations => {
  for (let registration of registrations) {
    registration.unregister();
  }
});

Replacing the line registering the service worker with a line unregistering it, “killed” the service worker but a hard refresh was necessary for this to take effect. After a hard refresh, the caching issues we had experienced were gone.

The key phrase is “After a hard refresh”. Before a hard refresh, the app appeared broken: only a static, white page was rendered. Opening Chrome’s developer tools, right-clicking the refresh button, and clicking “Empty Cache and Hard Reload” fixes this, but few users think to do this.

New visitors to the app are fine, but old users must hard refresh. I tried to think of a fix, but the best I could come up with was serving a Clear-Site-Data HTTP header. This should allow us to clear out the service worker without requiring a hard refresh (though I have not tested this). The downside is that it would probably screw up caching and we’d never know when to remove the header as old users could always be out there.

Luckily our app is internal and has a relatively small audience (of which only a minority were effected by this bug), but I wonder…how would a larger site deal with this? Imagine if Facebook rendered as a white screen for every user on the planet, would grandma be able to hard refresh?

P.S. Create React App no longer ships with an active service worker.