When Phil Karlton noted in the late 90’s that cache invalidation was a hard problem in computer science, he likely didn’t have service workers in mind!
Caching is hard. It is one of the biggest pain points in web development that we at Netlify aim to ease by automatically taking on that responsibility for you. Service Workers put unprecedented control into the hands of developers, enabling all sorts of offline-first and performance benefits. But they can also put the responsibility of managing caches back into the hands of the developer. And they can be rather complex. So much so that one of Netlify’s top support ticket causes is service workers!
What is a service worker?
We’ll quote Google:
A service worker is a script that your browser runs in the background, separate from a web page, opening the door to features that don’t need a web page or user interaction. The core feature… is the ability to intercept and handle network requests, including programmatically managing a cache of responses … [which] allows you to support offline experiences. — Google Web Fundamentals
A service worker sits “between” your app and the web, managing a cache of data and resources offline, giving your app the “progressive” user experience no matter how bad your connection is.
Who caches the cachers?
Service workers actually manage their caches fine. The problem comes when browsers try to cache the service workers.
Then all hell breaks loose, especially since the developer can’t see the service worker unless they go looking for it.
Browsers cache service workers for 24 hours by default, and don’t update them when you reload your page. As a developer you can remember to manually clear the cache or check Update on Reload in Chrome, but your users won’t be so lucky — when you make breaking changes to your APIs (if you even so much as move one file) that your service worker relies on, your users who still have your app open will refresh and see big scary errors like these:
When a stale endpoint request is combined with a catch-all file serving strategy that serves index.html
for all routes (a common Single-Page App setup), the browser could try to parse your HTML as a JavaScript file or JSON object, resulting in unhelpful errors like:
unexpected token '<'
Even worse for your support team, when users report these issues, time will have passed or their local environment will be different, so they won’t be able to reproduce these errors! Isn’t that fun! (Narrator: no, it was not fun.)
Set your cache headers
To solve these issues, you need to set a cache-control header for your service worker file:
cache-control: max-age=0,no-cache,no-store,must-revalidate
For Netlify users, this means setting a Custom Header like so:
/path/to/serviceworker.js
cache-control: max-age=0,no-cache,no-store,must-revalidate
Alternatively, like every other Netlify setting you can set this up to be automatically configured in your templates within your netlify.toml file:
[[headers]]
for = "/path/to/serviceworker.js"
[headers.values]
cache-control = "max-age=0,no-cache,no-store,must-revalidate"
This tells your browser to always check the server for updated versions of your service worker file instead of waiting for the default expiry time (usually 24 hours). Now your service workers will at least be up to date whenever they themselves try to make requests!
updateViaCache in Registration
We can’t break the progressive web, but there have been moves toward making service workers work like this without having to fiddle with cache headers. In Chrome 68 and above, you can pass updateViaCache: 'none'
when registering your service workers:
navigator.serviceWorker.register('/sw.js', {
updateViaCache: 'none' // this is new
});
We aren’t sure about other browsers supporting this API at this time.
Spread Awareness!
In light of all this confusion, we have decided to anoint Sept 21 as International Service Worker Caching Awareness Day!
(Fun fact, on this day in 1866 H.G. Wells was born, and we’d like to think he’d like Service Workers as the stuff of science fiction!)
Did you know you were caching service workers? Do you have better ways of dealing with service worker expiry than what we have described? What other trip-ups have you experienced? Hit us up on the Netlify Twitter and we’ll RT your tips and war stories!
P.S. we also recommend this Rich Harris gist on other handy Service Worker gotchas!