3

When I update the service worker, I close and reopen the page and I get the console log that the new service worker has been installed and activated, and the old cache has been deleted. However, the files listed to be added to the cache are not fetched from the server again, they are stale versions from hours ago. There's no way to make it update the files short of clearing storage in the Chrome dev tools.

How can I make my service worker populate it's cache with fresh files when a new version is installed?

For example, "stylesheet.css" is still an old version when the service worker updates:

let cacheName = "abc_web_v1_15";

self.addEventListener('install', (event) => {
    console.log('[Service Worker] Install');
    event.waitUntil(
        caches.open(cacheName).then(function (cache) {
            return cache.addAll([
                "/".
                "/stylesheet.css",
                ...
            ]);
        })
    );
});
self.addEventListener('fetch', (e) => {
    e.respondWith(
        caches.match(e.request).then(function (r) {
            console.log('[Service Worker] Fetching resource: ' + cacheName + ' ' + e.request.url);
            if (r) {
                console.log(cacheName + ' returning cached  ' + e.request);
                return r;
            } else {
                console.log(cacheName + ' returning fetched  ' + e.request);
                return fetch(e.request);
            }
        })
    )
});

self.addEventListener('activate', (e) => {
    console.log('[Service Worker] Activate');
    e.waitUntil(
        caches.keys().then((keyList) => {
            return Promise.all(keyList.map((key) => {
                if (cacheName.indexOf(key) === -1) {
                    console.log(`[Service Worker] Removing Cached Files from Cach-${key}`);
                    return caches.delete(key);
                }
            }));
        })
    );
});
Steve M
  • 9,296
  • 11
  • 49
  • 98

3 Answers3

5

The problem is very simple: when your Service Worker requests the "new" files, that fetch request goes through the regular browser HTTP cache. If the resource is found there, browser automatically returns that and doesn't go to network.

If you look at this snippet from your code:

return cache.addAll([
                "/".
                "/stylesheet.css",
                ...
            ]);

you'll see that your stylesheet's name is always the same no matter the content. In other words, when your stylesheet updates, the filename is still the same. For this reason the file is already in the browser's HTTP cache and thus SW gets it when it asks for it.

You should version your files. Usually hash of the file contents is used. With hash naming you end up with names like stylesheet.iudfirio132.css and that name changes every time the file changes even one character. This way new version of your application always uses resources with different names. You should also have build tooling like Workbox precaching to update the resource list in your Service Worker file so that you don't need to do it manually. More: https://developers.google.com/web/tools/workbox/modules/workbox-precaching

To completely grasp the subject here I recommend you to read this blog post, "Caching best practices & max-age gotchas" https://jakearchibald.com/2016/caching-best-practices/

pate
  • 4,937
  • 1
  • 19
  • 25
1

I keep this self-destructing Service Worker handy for when I need to clear out all old Service Workers and Service Worker caches.

I find it a really useful tool.

self.addEventListener('install', event => self.skipWaiting());

self.addEventListener('activate', event => {

  // Delete all Service Worker Caches
  caches.keys().then(cacheNames => {for (let name of cacheNames) {caches.delete(name);}});

  // Unregister all Service Workers
  self.registration.unregister()

    .then(() => self.clients.matchAll())

    .then((clients) => clients.forEach(client => client.navigate(client.url)))

});

Further Reading:

Rounin
  • 27,134
  • 9
  • 83
  • 108
  • Wouldn't this just infinitely keep reloading the page? – Willie Mar 29 '22 at 20:26
  • 1
    I see what you mean. I have been so busy looking at window.location.reload for other solutions that my brain just took client.navigate and made it into reloading... thanks! – Willie Mar 29 '22 at 21:17
  • For me, this returned an infinite loop of having your app refresh after the service worker became activated... – Willie Mar 30 '22 at 00:55
0

I answered a similar issue here: https://stackoverflow.com/a/64880568/7626299

As pate mentioned, even though you're updating your service worker with new code, a new cache name, calling self.skipWaiting(), he's still putting in cache the old resources in cache!

Actually you can rewrite your install event handler and get rid of cache.addAll. To solve this issue you need to fetch each resource by setting the cache option to reload so the browser will use the network to get the resource and not the cache.

const req = new Request(url, { cache: 'reload' });

More details here.

adrienv1520
  • 131
  • 1
  • 3