Pushpad
Articles › Engineering, Troubleshooting

Service worker: importScripts never updates scripts

  • # service-worker

Pushpad is a service for web push notifications. In order to add push notifications to their websites, our customers need to install the service worker that we provide on their websites.

Initially we just asked customers to copy and paste the whole content of the service worker. However this meant that for every update we made to the script, our customers had to take action.

Then we discovered importScripts and asked all our customers to do one last update to their service worker by replacing all its content with this simple line:

importScripts('https://pushpad.xyz/service-worker.js');

Finally something that could deliver modularization to the service workers!

Then we make some changes to the imported script confident that all the end users who subscribed to the push notifications will receive the update automatically. However that doesn't happen. Yep... the service worker is byte to byte identical so the browsers don't update it: they don't notice that the imported script has actually changed. Even the update which happens every 24 hours and serviceWorkerRegistration.update() are completely useless. All the HTTP headers returned (for the imported script or the service worker) are also ignored.

This means that scripts imported with importScripts inside the service worker will never ever get updates. The only workaround is to add some dummy content to the main code of the service worker to force the update on all clients:

// new dummy code
importScripts('https://pushpad.xyz/service-worker.js');

Since the current behavior is unusual and confusing we hope that the future specs of the standard will fix this.

The easiest solution for the developers would be to have browsers check for updates also in the imported scripts every time they check for updates for the service worker.

One option is to extend byte-to-byte comparison to the whole imported scripts (importScripts) graph. If one node has change, update.

The cons is that every check for updates triggers multiple HTTP requests (not a big deal if proper HTTP cache headers are used).

Another solution would be to have a method like serviceWorkerRegistration.update({force: true}) or serviceWorkerRegistration.update({updateScripts: true}). This would allow a service like Pushpad to deliver updates triggering a push signal that on reception would invoke that method. The cons of this is that it looks like an hack, it is much more complex than the previous solution and can be potentially abused forcing updates also when it's not needed.

UPDATE 2016 / 08: after some discussion on Github, the proposal described in this blog post has been taken into consideration and will become part of the standard in the next months. So in the future the byte to byte check will be extended to all imported scripts. This means that developers won't have to worry anymore because the imported scripts will be updated automatically in the same way as the main script.