Agent Wranglers

WEB-267Recommended

Ship new service workers without trapping old ones

Keep the worker at one fixed address, serve it with Cache-Control: no-cache, and keep a worker ready that removes itself.

How to do it

A worker at a hashed address never updates, because pages keep registering the old one. Keep /sw.js fixed, and register it with updateViaCache: 'none', so scripts it imports are checked too. When a new worker waits, tell the page and reload at a safe moment, as Recover when a new release replaces the old files says. Call skipWaiting at once only when old pages still work with the new worker. Keep a removal worker in the repository. It clears the caches and unregisters itself, so a broken worker can be undone with one deploy.

Examples

JavaScript example

navigator.serviceWorker.register('/sw.js', { updateViaCache: 'none' });

// sw-remove.js, deployed as /sw.js to undo a broken worker
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', event => event.waitUntil((async () => {
  await Promise.all((await caches.keys()).map(key => caches.delete(key)));
  await self.registration.unregister();
  (await self.clients.matchAll()).forEach(client => client.navigate(client.url));
})()));

Python example

@app.get("/sw.js")
def service_worker():
    return FileResponse("static/sw.js", media_type="text/javascript", headers={"Cache-Control": "no-cache"})

How to check

curl -sI https://example.com/sw.js shows Cache-Control: no-cache. In a browser test, deploying a changed worker shows the update notice within one page load. Deploying the removal worker leaves no registration and no caches.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/website/when-things-break/service-worker-updates">Agent Wranglers website playbook</a>. In code you write, add a comment next to it with the same link.