# Service worker registration and scope

> How navigator.serviceWorker.register() works, what scope controls, and how to align registration with your URL structure.

**In one line:** `navigator.serviceWorker.register(scriptURL, { scope })` installs a service worker that intercepts `fetch` events for every URL whose path starts with the scope. The scope defaults to the directory containing the worker script and can only be narrowed, not widened beyond that, unless the server sends a `Service-Worker-Allowed` header.

## How registration works

Calling `register()` is idempotent — the browser ignores the call if the identical script is already registered for that scope. The returned promise resolves to a `ServiceWorkerRegistration` object regardless of whether a new install was triggered.

```js
if ('serviceWorker' in navigator) {
  navigator.serviceWorker
    .register('/sw.js', { scope: '/' })
    .then((registration) => {
      console.log('Registered. Scope:', registration.scope);
    })
    .catch((err) => {
      console.error('Registration failed:', err);
    });
}
```

Registration should happen after the page's main content loads, not at the top of `<head>`, to avoid competing with critical resources.

## What scope controls

The scope is a URL path prefix. A service worker registered with scope `/app/` intercepts:

- `/app/`
- `/app/dashboard`
- `/app/settings/profile`

But not:

- `/` (root)
- `/blog/`
- `/app-launcher/` (different prefix)

The scope does not restrict what URLs the worker can *request* via `fetch()` — it only limits which page navigations and subresource requests the worker is *offered* to handle.

## Default scope

If no `scope` option is supplied, the scope defaults to the directory containing the worker script:

- Worker at `/sw.js` → default scope `/`
- Worker at `/app/sw.js` → default scope `/app/`

Placing the worker at the root (`/sw.js`) gives the widest default scope.

## Widening scope with `Service-Worker-Allowed`

A worker at `/app/sw.js` cannot normally claim `/` — its scope is bounded by its own path. To grant a wider scope, the server must include the `Service-Worker-Allowed` response header when the browser fetches the worker script:

```
Service-Worker-Allowed: /
```

Without this header, registering `/app/sw.js` with `{ scope: '/' }` throws a `DOMException`.

## Multiple registrations

A single origin can have more than one registration. This allows different workers for different sub-apps:

```js
// Worker A covers the marketing site
navigator.serviceWorker.register('/sw-marketing.js', { scope: '/marketing/' });

// Worker B covers the app shell
navigator.serviceWorker.register('/sw-app.js', { scope: '/app/' });
```

Scopes may overlap — the browser matches the most specific (longest) scope when determining which worker handles a request. Keeping scopes non-overlapping is an operational best practice, not a registration constraint.

## `updateViaCache`

By default the browser applies its own HTTP cache when fetching the worker script, which can delay updates if the script is aggressively cached. Set `updateViaCache: 'none'` to always bypass HTTP cache for the worker script itself:

```js
navigator.serviceWorker.register('/sw.js', {
  scope: '/',
  updateViaCache: 'none',
});
```

The W3C Service Workers specification defines `updateViaCache` as controlling whether the HTTP cache is consulted for the worker script (`'all'`), its imported scripts (`'imports'`, the default), or neither (`'none'`).

## Practical checklist

- [ ] Register after `DOMContentLoaded` or in a `load` event to avoid resource contention.
- [ ] Place the worker script as high in the path hierarchy as the scope you need (`/sw.js` for `/`, `/app/sw.js` for `/app/`).
- [ ] Use `updateViaCache: 'none'` on the registration so the browser always checks for a new worker version.
- [ ] Log `registration.scope` in development to confirm the worker covers the expected paths.
- [ ] For multi-app sites, define non-overlapping scopes and deploy separate workers per scope.

## Cross-references

- [Service worker lifecycle](/reference/service-worker/lifecycle/) — what happens after registration (install, activate, waiting)
- [The update flow and skipWaiting](/reference/service-worker/update-skipwaiting/) — how new versions replace old ones
- [Manifest scope](/reference/manifest/scope/) — the related concept in the Web App Manifest