17

I have a service worker script that is registering repeatedly over multiple site levels.

In other words the same service worker is registered for www.site.ca/, www.site.ca/text-text, www.site.ca/example-example, and etc.

The site is built on php with different urls being produced depending on the content so kind of API-like. The reason that the service worker registration is on those pages is because most of the site traffic lands on those pages rather than the home page. The result being the same service worker being registered with different ids over different pages.

Does anyone have a way to prevent multiple registrations of the same script over multiple sub levels?

EDIT:

The purpose of the service worker is to set up notifications even if the user isn't on the site. Also the issue I'm having would negatively affect any attempts to update the service worker in the future since there would be multiple copies to update. The result being that only one copy would ever be updated when the user lands in the site again on a specific page. There would be no guarantee that all the copies of that service worker file would be updated. Also the result of the multiple copies has resulted in users getting stacks of notifications from the site so it is a problem.

EDIT:

The below is what I have so far and to my understanding scope defines where it gets registered. However the problem is that it needs to be able to register wherever a person enters the site thus the scope was set to '/'.

The issue I want to solve is to prevent subsequent registers of the same service worker file.

Localhost is a placeholder for the actual site.

navigator.serviceWorker.register('localhost/service-worker.js', {scope: './'})
.then(initialiseState);

In Chrome dev tools for debugging, the resulting service workers are in the format like this:

Scope: localhost/url/stuff/
Registration ID: 110
Active worker:
Installation Status: ACTIVATED
Running Status: STOPPED
Script: localhost/service-worker.js
...

Scope: localhost/
Registration ID: 111
Active worker:
Installation Status: ACTIVATED
Running Status: STOPPED
Script: localhost/service-worker.js
...

Scope: localhost/url
Registration ID: 112
Active worker:
Installation Status: ACTIVATED
Running Status: STOPPED
Script: localhost/service-worker.js
...

Ideally I want to somehow just keep it to one entry somehow.

KROrb
  • 181
  • 1
  • 1
  • 7

2 Answers2

22

In the site-with-sub-directories scenario, where you want all of your sub-pages to be controlled by the same top-level service worker, I would suggest using an absolute URL for the service worker script location, and then relying on the default behavior that takes place when options.scope is omitted.

Note that the MDN docs quoted in the other response were incorrect about the default behavior. The service worker specification was updated in January 2015 to change the default scope to be equivalent to new URL('./', serviceWorkerScriptUrl).toString(). (The MDN docs have since been updated and should be accurate.)

To illustrate why I recommend omitting options.scope in this use case, consider the following alternatives, assuming you want a single, shared service worker registration:

  • navigator.serviceWorker.register('/sw.js') will give you a registration with a scope of /, which is what you want.
  • navigator.serviceWorker.register('/sw.js', {scope: '/'}) will give you a registration with a scope of /, which is also what you want, but doesn't offer any benefits over what you get with the default behavior.
  • navigator.serviceWorker.register('/subsite/sw.js', {scope: '/'}) will fail to register, because the scope of / is too broad for a service worker script that lives at /subsite/sw.js.
  • navigator.serviceWorker.register('/subsite/sw.js') will give you a registration with a scope of /subsite, which is what you want.
  • navigator.serviceWorker.register('/subsite/sw.js', {scope: '/subsite'}) would work, but again, it doesn't give you any benefits over the default behavior.

The only times I'd recommend explicitly setting a value for options.scope is when you need to explicitly limit the scope of the service worker to be narrower than the default scope.

Note that if you do decide to provide a value for options.scope, and that value is a relative URL like './', the URL is interpreted as being relative to the location of the script/page calling register(). {scope: './'} is not interpreted as being relative to the location of the service worker script URL. I was confused by this point for a while, so take some time to fully understand the implications there. That explains why you ended up with multiple registrations in your initial question.

Jeff Posnick
  • 53,580
  • 14
  • 141
  • 167
15

According to MDN, ServiceWorkerContainer.register method takes a second parameter of options:

Currently available options are:

  • scope: A USVString representing a URL that defines a service worker's registration scope; what range of URLs a service worker can control. This is usually a relative URL, and it defaults to '/' when not specified.

As for your updated question, the W3C Service Workers WD says:

A service worker registration of an identical scope url when one already exists in the user agent causes the existing service worker registration to be replaced.

Therefore navigator.serviceWorker.register(url, {scope: '/'}).then(doSomething) should work.

Problem is with the scope URL in your example:

./ should be /

  • My understanding is that scope would limit registration but wouldn't work well on the site I'm working on. The urls are generated based on certain parameters and isn't always something constant like /about or /contact us. And those pages each are a kind of landing page since the most likely user search that leads to the site are the parameters that make up the url. So the service worker would need to be registered no matter where a user was and my aim to make sure only one copy of that service worker. – KROrb Nov 20 '15 at 23:44
  • 2
    Service workers - just as appcache - should be linked to & registered in all the pages of your application. The problem above is, indeed, that you specify a *relative* scope (`./`), which forces new service worker instances to be created, regardless of the service worker script itself being the same. If you want to have the same instance to be attached to all pages, regardless of the path, you should use absolute/root pathes, such as `/` for the scope. – Flaki Nov 21 '15 at 14:33