How to make ServiceWorker survive cache reset/Shift+F5? - javascript

I want to create a website that can work even when it's server is offline — I found that that's what ServiceWorkers are for.
When I reload the page with service worker and no connectivity, it works just fine. However, shift+reload (e.g. bypassing cache) disarms service worker and I get "could not connect to server" error.
My question is — can I somehow prevent shift+reload (shift+f5, ctrl+f5 etc) from ruining service worker, or, at least, make it recover afterwards without restoring connectivity?

I was able to keep using the service worker even after Ctrl+F5 via the following approach:
In the window script:
navigator.serviceWorker.register(<...>).then (registration => {
if (navigator.serviceWorker.controller === null) {
// we get here after a ctrl+f5 OR if there was no previous service worker.
navigator.serviceWorker.ready.then(() => {
registration.active.postMessage("claimMe");
});
}
<...>
});
In the service script:
self.onmessage = (event) => {
if (event.data === "claimMe") {
self.clients.claim();
}
};
In short, we ask the service worker to claim all clients again, which works even for clients that used Ctrl+F5.
If you want to respect the Ctrl+F5 in the service worker code, you could either:
Clear the cache before claiming. Note that this will also affect any other existing clients, which may be unwanted.
Do something more complicated like sending the id of the client that requested a Ctrl+F5 and treat fetch requests specially for such clients.

QUICK Answer
How to make ServiceWorker survive cache reset/Shift+F5?
Theorically (*), you can do it with js plugins which detect the key hit of Ctrl or Shift... then prevent the "force refresh" to happen
...but there is a story behind this Ctrl/Shift reload.
(*) disclaimer: I've not tried this yet
LONG story... (kind of)
This is actually a spec of Service Worker. And only present in recent change of Chrome. For the earlier version of Chrome , Service Worker has no any issue surviving a "force refresh".
Along with the spec from W3C
navigator.serviceWorker.controller returns null if the request is a force refresh (shift+refresh).
Also, there are many people (**) has suggested that a "force refresh" should always clear out all kind of caches. Which is matched with the purpose of its existent and its spec.
...Furthermore, we even got it on the wikipedia...
Wikipedia: Bypassing your cache means forcing your web browser to re-download a web page from scratch
(**) us, web developers in the early stage of service worker.
In my opinion I think it is OK to let a force refresh doing its job. Since pretty much all of us always expecting the browser to not use any cache when we doing this.

I was able to solve this by detecting the ctrl+shift+r and reloading:
const wb = new Workbox(swUrl)
const wbReg = await wb.register({ immediate: true })
// workaround for ctrl + shift + r disabling service workers
// https://web.dev/service-worker-lifecycle/#shift-reload
if (wbReg && navigator.serviceWorker.controller === null) {
console.error('detected ctrl+shift+r: reloading page')
location.reload()
throw new Error('page loaded with cache disabled: ctrl+shift+r')
}

Related

How to clear a service worker cache for index.html page? [duplicate]

I'm playing with the service worker API in my computer so I can grasp how can I benefit from it in my real world apps.
I came across a weird situation where I registered a service worker which intercepts fetch event so it can check its cache for requested content before sending a request to the origin.
The problem is that this code has an error which prevented the function from making the request, so my page is left blank; nothing happens.
As the service worker has been registered, the second time I load the page it intercepts the very first request (the one which loads the HTML). Because I have this bug, that fetch event fails, it never requests the HTML and all I see its a blank page.
In this situation, the only way I know to remove the bad service worker script is through chrome://serviceworker-internals/ console.
If this error gets to a live website, which is the best way to solve it?
Thanks!
I wanted to expand on some of the other answers here, and approach this from the point of view of "what strategies can I use when rolling out a service worker to production to ensure that I can make any needed changes"? Those changes might include fixing any minor bugs that you discover in production, or it might (but hopefully doesn't) include neutralizing the service worker due to an insurmountable bug—a so called "kill switch".
For the purposes of this answer, let's assume you call
navigator.serviceWorker.register('service-worker.js');
on your pages, meaning your service worker JavaScript resource is service-worker.js. (See below if you're not sure the exact service worker URL that was used—perhaps because you added a hash or versioning info to the service worker script.)
The question boils down to how you go about resolving the initial issue in your service-worker.js code. If it's a small bug fix, then you can obviously just make the change and redeploy your service-worker.js to your hosting environment. If there's no obvious bug fix, and you don't want to leave your users running the buggy service worker code while you take the time to work out a solution, it's a good idea to keep a simple, no-op service-worker.js handy, like the following:
// A simple, no-op service worker that takes immediate control.
self.addEventListener('install', () => {
// Skip over the "waiting" lifecycle state, to ensure that our
// new service worker is activated immediately, even if there's
// another tab open controlled by our older service worker code.
self.skipWaiting();
});
/*
self.addEventListener('activate', () => {
// Optional: Get a list of all the current open windows/tabs under
// our service worker's control, and force them to reload.
// This can "unbreak" any open windows/tabs as soon as the new
// service worker activates, rather than users having to manually reload.
self.clients.matchAll({type: 'window'}).then(windowClients => {
windowClients.forEach(windowClient => {
windowClient.navigate(windowClient.url);
});
});
});
*/
That should be all your no-op service-worker.js needs to contain. Because there's no fetch handler registered, all navigation and resource requests from controlled pages will end up going directly against the network, effectively giving you the same behavior you'd get without if there were no service worker at all.
Additional steps
It's possible to go further, and forcibly delete everything stored using the Cache Storage API, or to explicitly unregister the service worker entirely. For most common cases, that's probably going to be overkill, and following the above recommendations should be sufficient to get you in a state where your current users get the expected behavior, and you're ready to redeploy updates once you've fixed your bugs. There is some degree of overhead involved with starting up even a no-op service worker, so you can go the route of unregistering the service worker if you have no plans to redeploy meaningful service worker code.
If you're already in a situation in which you're serving service-worker.js with HTTP caching directives giving it a lifetime that's longer than your users can wait for, keep in mind that a Shift + Reload on desktop browsers will force the page to reload outside of service worker control. Not every user will know how to do this, and it's not possible on mobile devices, though. So don't rely on Shift + Reload as a viable rollback plan.
What if you don't know the service worker URL?
The information above assumes that you know what the service worker URL is—service-worker.js, sw.js, or something else that's effectively constant. But what if you included some sort of versioning or hash information in your service worker script, like service-worker.abcd1234.js?
First of all, try to avoid this in the future—it's against best practices. But if you've already deployed a number of versioned service worker URLs already and you need to disable things for all users, regardless of which URL they might have registered, there is a way out.
Every time a browser makes a request for a service worker script, regardless of whether it's an initial registration or an update check, it will set an HTTP request header called Service-Worker:.
Assuming you have full control over your backend HTTP server, you can check incoming requests for the presence of this Service-Worker: header, and always respond with your no-op service worker script response, regardless of what the request URL is.
The specifics of configuring your web server to do this will vary from server to server.
The Clear-Site-Data: response header
A final note: some browsers will automatically clear out specific data and potentially unregister service workers when a special HTTP response header is returned as part of any response: Clear-Site-Data:.
Setting this header can be helpful when trying to recover from a bad service worker deployment, and kill-switch scenarios are included in the feature's specification as an example use case.
It's important to check the browser support story for Clear-Site-Data: before your rely solely on it as a kill-switch. As of July 2019, it's not supported in 100% of the browsers that support service workers, so at the moment, it's safest to use Clear-Site-Data: along with the techniques mentioned above if you're concerned about recovering from a faulty service worker in all browsers.
You can 'unregister' the service worker using javascript.
Here is an example:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
//returns installed service workers
if (registrations.length) {
for(let registration of registrations) {
registration.unregister();
}
}
});
}
That's a really nasty situation, that hopefully won't happen to you in production.
In that case, if you don't want to go through the developer tools of the different browsers, chrome://serviceworker-internals/ for blink based browsers, or about:serviceworkers (about:debugging#workers in the future) in Firefox, there are two things that come to my mind:
Use the serviceworker update mechanism. Your user agent will check if there is any change on the worker registered, will fetch it and will go through the activate phase again. So potentially you can change the serviceworker script, fix (purge caches, etc) any weird situation and continue working. The only downside is you will need to wait until the browser updates the worker that could be 1 day.
Add some kind of kill switch to your worker. Having a special url where you can point users to visit that can restore the status of your caches, etc.
I'm not sure if clearing your browser data will remove the worker, so that could be another option.
I haven't tested this, but there is an unregister() and an update() method on the ServiceWorkerRegistration object. you can get this from the navigator.serviceWorker.
navigator.serviceWorker.getRegistration('/').then(function(registration) {
registration.update();
});
update should then immediately check if there is a new serviceworker and if so install it. This bypasses the 24 hour waiting period and will download the serviceworker.js every time this javascript is encountered.
For live situations you need to alter the service worker at byte-level (put a comment on the first line, for instance) and it will be updated in the next 24 hours. You can emulate this with the chrome://serviceworker-internals/ in Chrome by clicking on Update button.
This should work even for situations when the service worker itself got cached as the step 9 of the update algorithm set a flag to bypass the service worker.
We had moved a site from godaddy.com to a regular WordPress install. Client (not us) had a serviceworker file (sw.js) cached into all their browsers which completely messed things up. Our site, a normal WordPress site, has no service workers.
It's like a virus, in that it's on every page, it does not come from our server and there is no way to get rid of it easily.
We made a new empty file called sw.js on the root of the server, then added the following to every page on the site.
<script>
if (navigator && navigator.serviceWorker && navigator.serviceWorker.getRegistration) {
navigator.serviceWorker.getRegistration('/').then(function(registration) {
if (registration) {
registration.update();
registration.unregister();
}
});
}
</script>
In case it helps someone else, I was trying to kill off service workers that were running in browsers that had hit a production site that used to register them.
I solved it by publishing a service-worker.js that contained just this:
self.globalThis.registration.unregister();

Situations in which service worker may display old version of app even after new version is loaded?

Sorry, I don't have a reproducible test case of this issue because I've never even seen it happen myself. I only know it happens because of client-side logging in my app and complaints from users.
The problem is:
I deploy a new version of my app
User visits my site, gets the new version, and runs it successfully
User visits my site again and gets an old version of my app
I'm using a service worker, which I was hoping could provide some guarantees about that scenario not happening. It's particularly troubling when the new version includes an IndexedDB schema migration, because then old version of my app won't even work anymore.
More details:
I'm using Workbox 4.3.1. My service worker is basically:
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
workbox.precaching.precacheAndRoute([]);
workbox.routing.registerNavigationRoute("/index.html", {
blacklist: [
new RegExp("^/static"),
new RegExp("^/sw.js"),
],
});
workbox.precaching.precacheAndRoute([]); gets filled in by workboxBuild.injectManifest. I can manually confirm that the right files get filled in. And generally the service worker works. I can see it in the browser dev tools. I can disconnect from the Internet and still use my app. Everything seems fine. Like I said above, I've never seen this problem happen, and I don't have a reproducible test case.
But some of my users experienced the problem described above. I tried to use client-side error logging to investigate. I added some code to my app to store its version number in localStorage, and on initial load it compares that version number against the current running version number. If the version in localStorage is more recent than the current running version (i.e., it successfully ran a newer version in the past but now is going back to an older version), it logs the version numbers along with some additional information:
let registrations = [];
if (window.navigator.serviceWorker) {
registrations = await window.navigator.serviceWorker.getRegistrations();
}
log({
hasNavigatorServiceWorker:
window.navigator.serviceWorker !== undefined,
registrationsLength: registrations.length,
registrations: registrations.map(r => {
return {
scope: r.scope,
active: r.active
? {
scriptURL: r.active.scriptURL,
state: r.active.state,
}
: null,
installing: r.installing
? {
scriptURL: r.installing.scriptURL,
state: r.installing.state,
}
: null,
waiting: r.waiting
? {
scriptURL: r.waiting.scriptURL,
state: r.waiting.state,
}
: null,
};
}),
})
Looking in my logs, I see that this problem occurs for only like 1% of my users. Firefox is enriched among these users (4% of overall traffic, but 18% of log entries for this problem), but it happens for all browsers and operating systems.
And I see that almost all records have these values:
{
hasNavigatorServiceWorker: true,
registrationsLength: 1,
registrations: [{
"scope": "https://example.com/",
"active": {
"scriptURL": "https://example.com/sw.js",
"state": "activated"
},
"installing": null,
"waiting": null
}]
}
As far as I know, those are all correct values.
I should also note that my JavaScript files have a hash in the URL, so it cannot be that my server is somehow returning an old version of my JavaScript when the user requests a new version.
So, what could be going on? How can this observed behavior be explained? What else could I be logging to debug this further?
The only scenarios I can come up with seem highly implausible to me. Like...
User loads v1, service worker fails for some reason
User loads v2, service worker fails for some reason
User somehow gets v1 from their browser cache, since all prior service workers failed, but now the service worker works correctly and stores this as the current version
But I have no evidence of the service worker ever failing. Nothing in my error logs. No user complaining that offline support is broken.
If it helps, the actual website where this happens is https://play.basketball-gm.com/, the service worker is at https://play.basketball-gm.com/sw.js, and all the code is available on GitHub.
And this problem has been going on ever since I started using a service worker, about a year ago. I am just finally getting around to writing up a Stack Overflow question, because I've given up hope that I'll be able to figure it out on my own or even create a reproducible test case.
Somehow user gets the old version from their cache. Removing outdated caches should do the trick. Once a new service worker has installed and a previous version isn't being used, the new one activates, and you get an activate event. Because the old version is out of the way, it's a good time to delete unused caches.
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
// Return true if you want to remove this cache,
// but remember that caches are shared across
// the whole origin
}).map(function(cacheName) {
return caches.delete(cacheName);
})
);
})
);
});
During activation, other events such as fetch are put into a queue, so a long activation could potentially block page loads. Keep your activation as lean as possible, only using it for things you couldn't do while the old version was active.
This may be more of a 'tech support' answer for troubled users, in my frame of thought.
I have run into similar issues personally when trying to re-load my own React applications locally, during development. (Client-Side code) I was using a tunneling service, ngrok.
My site would continue to function, even after I killed the service with an older version of the code. (I found this frustrating, after implementing a fix and it appearing to not work)
(Tech Support) Answer:
It may be necessary to perform a 'Hard Reload', sometimes a 'Empty Cache and Hard Reload', this should cleanse the browser. (It really depends on the structure of the files loaded with the site).
You can access these options while the developer console is open.
For more details on hard refresh, you can reference this SO post: whats-the-difference-between-normal-reload-hard-reload-and-empty-cache-a
A year later and I've finally figured it out.
I was precaching an asset that was blocked by some ad blockers. This caused service worker installation to fail, keeping the old service worker around longer than intended. Somehow this failure was not caught by my client side error logging.

How do I detect in a service worker fetch that the request is for the manifest's start_url?

I have a PWA, which has a manifest.json with a start_url.
I have a service worker with a fetch event that only caches certain requests.
This is done in the service worker by overriding the response to proxy from the cache (TypeScript for clarity):
self.addEventListener('fetch', (event: FetchEvent) => {
// This MUST be synchronous until respondWith is called
const cache = isCachable(event.request);
if (!isCachable)
return; // Don't proxy, let the network request continue
// Kick off a promise to check the cache, and clone/cache the response otherwise
const proxy: Promise<Response> = cacheResponse(event);
event.respondWith(proxy);
}
I want to cache the start_url, which means isCachable above needs to be able to tell that the value of start_url is the relative route being requested.
I can load the manifest.json in the SW but that feels really clunky. I can hard code the values in the SW, but then I need to update two places if config changes.
In the fetch handler event.request.url is absolute, but the start_url is relative to the manifest.json - so, for instance, I might have:
manifest.json: { "start_url": "appshell" ... }
Web site gets deployed to www.example.com/folder/ (but might be deployed to sub.example.com or www.example.com/a/b or whatever)
Online user visits site, either visits everything or install script caches direct.
Later, same user visits offline.
The fetch above fires with event.request.url === 'www.example.com/folder/appshell'
I need the isCachable function to be able to tell that resource should be cached synchronously. It needs to determine that www.example.com/folder/appshell is appshell (resolve the relative link) and that appshell is set as start_url (read the manifest).
Obviously, all this can be hard coded. However, every PWA needs respond from the cache for start_url, so this can't be a new problem. Before I reinvent the wheel is there a better way or something I'm missing?
So...
Given that service workers need the manifest, is there a good way to get the manifest's config in the worker?
Is there a way to resolve the relative link for comparison with the manifest?
I've figured out how to do this, but it's nasty and I'm sure there's a better way.
In particular I'm hard-coding in the manifest inline (I could fetch it, but that's nasty too).
const manifest = { start_url: 'appshell' };
Then I use self.location to get the path of the service worker. This includes the file name, so I trim that off the end:
// if file is 'sw.js' that's 5 chars off the end
const applicationRoot = new URL(self.location.href.slice(0, -5) + manifest.start_url);
Then I can check against this:
self.addEventListener('fetch', (event: FetchEvent) => {
// This MUST be synchronous until respondWith is called
const url = new URL(event.request.url);
if (url.pathname === applicationRoot.pathname) {
// Request is for the start_url, always return something so the PWA starts offline
event.respondWith(...):
}
// ... see question for rest
});
This is clunky, but at least it can always serve the cached start_url without caching everything else, so it is the accepted answer for now. I'd love to see a better way of doing this, ideally without hard coding/fetching the manifest.json so that config changes there don't require a new service worker.

Block chrome network requests until extension is initialized

I am working on proxy extension for chrome browser. My extension sets proxy config of the browser using:
chrome.proxy.settings.set({
value: config,
scope: 'regular',
});
Where config uses fixed_servers mode.
Proxy servers require authentication, therefore I have:
chrome.webRequest.onAuthRequired.addListener(details => {
// Some logic
return {
authCredentials: {
username: usernameValue,
password: passwordValue,
},
};
});
Up until latest 71st Chrome version this logic was working as expected:
Browser boots > extensions initialized > all traffic goes through proxy and auth requests from proxy server are handled by listener.
Since 71st version it seems that browser doesn't wait for extensions to be initialized(issue appears after Hard Quit, i.e. using command + Q) and starts sending requests. Since proxy config is already set:
Requests go through proxy > proxy server requests authentication > extension is still not initialized by browser, therefore auth request listener is not added in the background as well - since there is nothing to intercept auth requests - native auth prompt is sown for the user.
This ends up in a very bad UX + moments later when extension gets initialized, listener is already in place, so user either can fill the prompt and submit, or simply cancel - anyway proxy and its auth works.
I am looking for solution for this situation.
Maybe there is a way to set some config for the browser that prevents it from doing requests until certain extension is initialized, or some way to suspend/reset/clear proxy config before browser quit(then I could manually set proxy again on init). Or any other fix for a given situation.
I've already tried to us chrome.windows methods to monitor when browser windows are created and removed and on last one being removed tried calling chrome.proxy.settings.clear({ scope: 'regular' }, function() {...});, but as I figured out, only sync manage to happen before quit, while async do not, hence chrome.proxy.settings.clear() is of no use.
I am thankful in advance for any tips, suggestions, solutions/hacks and etc.

How can I remove a buggy service worker, or implement a "kill switch"?

I'm playing with the service worker API in my computer so I can grasp how can I benefit from it in my real world apps.
I came across a weird situation where I registered a service worker which intercepts fetch event so it can check its cache for requested content before sending a request to the origin.
The problem is that this code has an error which prevented the function from making the request, so my page is left blank; nothing happens.
As the service worker has been registered, the second time I load the page it intercepts the very first request (the one which loads the HTML). Because I have this bug, that fetch event fails, it never requests the HTML and all I see its a blank page.
In this situation, the only way I know to remove the bad service worker script is through chrome://serviceworker-internals/ console.
If this error gets to a live website, which is the best way to solve it?
Thanks!
I wanted to expand on some of the other answers here, and approach this from the point of view of "what strategies can I use when rolling out a service worker to production to ensure that I can make any needed changes"? Those changes might include fixing any minor bugs that you discover in production, or it might (but hopefully doesn't) include neutralizing the service worker due to an insurmountable bug—a so called "kill switch".
For the purposes of this answer, let's assume you call
navigator.serviceWorker.register('service-worker.js');
on your pages, meaning your service worker JavaScript resource is service-worker.js. (See below if you're not sure the exact service worker URL that was used—perhaps because you added a hash or versioning info to the service worker script.)
The question boils down to how you go about resolving the initial issue in your service-worker.js code. If it's a small bug fix, then you can obviously just make the change and redeploy your service-worker.js to your hosting environment. If there's no obvious bug fix, and you don't want to leave your users running the buggy service worker code while you take the time to work out a solution, it's a good idea to keep a simple, no-op service-worker.js handy, like the following:
// A simple, no-op service worker that takes immediate control.
self.addEventListener('install', () => {
// Skip over the "waiting" lifecycle state, to ensure that our
// new service worker is activated immediately, even if there's
// another tab open controlled by our older service worker code.
self.skipWaiting();
});
/*
self.addEventListener('activate', () => {
// Optional: Get a list of all the current open windows/tabs under
// our service worker's control, and force them to reload.
// This can "unbreak" any open windows/tabs as soon as the new
// service worker activates, rather than users having to manually reload.
self.clients.matchAll({type: 'window'}).then(windowClients => {
windowClients.forEach(windowClient => {
windowClient.navigate(windowClient.url);
});
});
});
*/
That should be all your no-op service-worker.js needs to contain. Because there's no fetch handler registered, all navigation and resource requests from controlled pages will end up going directly against the network, effectively giving you the same behavior you'd get without if there were no service worker at all.
Additional steps
It's possible to go further, and forcibly delete everything stored using the Cache Storage API, or to explicitly unregister the service worker entirely. For most common cases, that's probably going to be overkill, and following the above recommendations should be sufficient to get you in a state where your current users get the expected behavior, and you're ready to redeploy updates once you've fixed your bugs. There is some degree of overhead involved with starting up even a no-op service worker, so you can go the route of unregistering the service worker if you have no plans to redeploy meaningful service worker code.
If you're already in a situation in which you're serving service-worker.js with HTTP caching directives giving it a lifetime that's longer than your users can wait for, keep in mind that a Shift + Reload on desktop browsers will force the page to reload outside of service worker control. Not every user will know how to do this, and it's not possible on mobile devices, though. So don't rely on Shift + Reload as a viable rollback plan.
What if you don't know the service worker URL?
The information above assumes that you know what the service worker URL is—service-worker.js, sw.js, or something else that's effectively constant. But what if you included some sort of versioning or hash information in your service worker script, like service-worker.abcd1234.js?
First of all, try to avoid this in the future—it's against best practices. But if you've already deployed a number of versioned service worker URLs already and you need to disable things for all users, regardless of which URL they might have registered, there is a way out.
Every time a browser makes a request for a service worker script, regardless of whether it's an initial registration or an update check, it will set an HTTP request header called Service-Worker:.
Assuming you have full control over your backend HTTP server, you can check incoming requests for the presence of this Service-Worker: header, and always respond with your no-op service worker script response, regardless of what the request URL is.
The specifics of configuring your web server to do this will vary from server to server.
The Clear-Site-Data: response header
A final note: some browsers will automatically clear out specific data and potentially unregister service workers when a special HTTP response header is returned as part of any response: Clear-Site-Data:.
Setting this header can be helpful when trying to recover from a bad service worker deployment, and kill-switch scenarios are included in the feature's specification as an example use case.
It's important to check the browser support story for Clear-Site-Data: before your rely solely on it as a kill-switch. As of July 2019, it's not supported in 100% of the browsers that support service workers, so at the moment, it's safest to use Clear-Site-Data: along with the techniques mentioned above if you're concerned about recovering from a faulty service worker in all browsers.
You can 'unregister' the service worker using javascript.
Here is an example:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
//returns installed service workers
if (registrations.length) {
for(let registration of registrations) {
registration.unregister();
}
}
});
}
That's a really nasty situation, that hopefully won't happen to you in production.
In that case, if you don't want to go through the developer tools of the different browsers, chrome://serviceworker-internals/ for blink based browsers, or about:serviceworkers (about:debugging#workers in the future) in Firefox, there are two things that come to my mind:
Use the serviceworker update mechanism. Your user agent will check if there is any change on the worker registered, will fetch it and will go through the activate phase again. So potentially you can change the serviceworker script, fix (purge caches, etc) any weird situation and continue working. The only downside is you will need to wait until the browser updates the worker that could be 1 day.
Add some kind of kill switch to your worker. Having a special url where you can point users to visit that can restore the status of your caches, etc.
I'm not sure if clearing your browser data will remove the worker, so that could be another option.
I haven't tested this, but there is an unregister() and an update() method on the ServiceWorkerRegistration object. you can get this from the navigator.serviceWorker.
navigator.serviceWorker.getRegistration('/').then(function(registration) {
registration.update();
});
update should then immediately check if there is a new serviceworker and if so install it. This bypasses the 24 hour waiting period and will download the serviceworker.js every time this javascript is encountered.
For live situations you need to alter the service worker at byte-level (put a comment on the first line, for instance) and it will be updated in the next 24 hours. You can emulate this with the chrome://serviceworker-internals/ in Chrome by clicking on Update button.
This should work even for situations when the service worker itself got cached as the step 9 of the update algorithm set a flag to bypass the service worker.
We had moved a site from godaddy.com to a regular WordPress install. Client (not us) had a serviceworker file (sw.js) cached into all their browsers which completely messed things up. Our site, a normal WordPress site, has no service workers.
It's like a virus, in that it's on every page, it does not come from our server and there is no way to get rid of it easily.
We made a new empty file called sw.js on the root of the server, then added the following to every page on the site.
<script>
if (navigator && navigator.serviceWorker && navigator.serviceWorker.getRegistration) {
navigator.serviceWorker.getRegistration('/').then(function(registration) {
if (registration) {
registration.update();
registration.unregister();
}
});
}
</script>
In case it helps someone else, I was trying to kill off service workers that were running in browsers that had hit a production site that used to register them.
I solved it by publishing a service-worker.js that contained just this:
self.globalThis.registration.unregister();

Categories