Killing Old Service Workers for the Greater Good

Antonio Calapez
Hackages Blog
Published in
5 min readAug 29, 2017

--

Recently at Hackages, we ran into the following problem: we had our website at https://www.hackages.io and then we decided to migrate it to https://hackages.io. Sounds easy, right? Just writing a redirect from the old domain to the new one? It turned out it wasn’t as simple as we thought!

So the first thing we did was using NGINX to redirect all the traffic from https://www.hackages.io to https://hackages.io.

The thing we did not think about was that service workers don’t play nice with redirects.

The issue was the following:

The user would go on https://www.hackages.io and NGINX would send them a 301 redirect for every resource they’d send a request for.

This worked perfectly fine for new users, but users who had already visited the website would encounter some issues.

Service workers update themselves automatically if there’s a new version available. In our case it tried to get the new version on https://www.hackages.io/service-worker.js, which redirects to https://hackages.io/service-worker.js. So the service worker would try to update itself from a new service worker behind a redirect which caused the following error:

Since the service worker could not get the resources behind the redirect, users would just see the old version of the website served from the old service worker. And of course we didn’t want that to happen.

Reproducing the issue locally to tackle it down

I’ll now show you how we tackled this issue in the first place. Let’s build two NGINX docker images both using this conf (associated Docker files can be found here)

and using the following service worker:

We’ll run one of them on localhost:1500 (the old website, we’ll call it blue) and localhost:2000 (the new website, let’s call it red).

Now, let’s kill blue and run this NGIX conf instead to redirect all the trafic from blue to red.

New users will get the new version, no problem, but old users will still encounter the issue mentioned above: the service worker will try to get the new service worker behind a redirect, and that doesn’t work.

So now all our old users are stuck on blue and are not being redirected to red because the service worker hijacks it.

Destroying the old service-worker

The strategy to fix the problem will be the following: craft a service worker that is going to delete the previous one and serve that service worker in the NGINX that handles the redirect (blue).

We’ll use this NGINX conf to redirect everything but our specially crafted service worker:

The first version we had was the following:

Let’s break this down:

self.skipWaiting() forces the waiting service worker to become the active service worker, so even if a user has multiple tabs open it kicks in instantly. (If there are multiple tabs open for a same website, only one service worker runs for all of them).

The unregister method of the registration object will delete any service worker registered for the host:port combo the service worker is served on.

This pattern is going to kill any service worker that exists and old users will be redirected to red on their second visit on blue.

Improving our solution

This method works .. fine, but now let’s build a better version that will force the user to navigate to the new domain. Because in our case we did not want the users to still see the blue website, so we had to find a way to reload their browser after unregistering the service worker.

In a service worker you can’t simply do:

because you don’t have access to a lot of things in a service worker, including window.

So how to solve this then? First let’s grab the list of clients using:

This returns us a promise containing the list of clients of type window (tabs).

Each client will expose a navigate method that allows us to redirect the client to another page.

Here we make the client navigate to itself to reload the page!

Putting it all together:

To recap, this is what our service worker will do:

  • It’s going to instantly activate itself
  • It’s going to tell the service worker to unregister itself
  • It’s going to refresh each tab of the user
  • NGINX will send a 301 redirect and the user’ll navigate from blue to red

We’re really happy with this solution because the version of the website available @ www.hackages.io was really outdated. While I agree that force-reloading the browser of the user may not be the best solution it’s the only one we found to make sure no one would still end up on the old website.

In action:

Conclusion

That’s it, in this post I explained briefly how I resolved the problem of an old service worker hijacking the redirect for old users. The solution was to serve a specially crafted service worker on our old domain that’d delete itself and navigate to the new website.

I hope I can help people with similar problems with these solutions.

All the resources used in this blogpost can be found here: (https://github.com/0xClpz/migrating-service-workers-from-an-old-domain).

If you have a better answer to this issue or you’d like to add on this solution, feel free to drop me a line!

If you enjoyed reading this article, please show me with a clap or a share! It motivates me to keep writing more, thanks!

You can find more information about Hackages on our website. We‘re a community driven company that offers high level training on the latests frameworks and technologies around JavaScript.

--

--