HTTPS and Secure Contexts
Why secure contexts matter for PWAs
Section titled “Why secure contexts matter for PWAs”Service workers are powerful. They intercept every network request your page makes, can modify responses, and persist across browser sessions. That power comes with a mandatory security requirement: service workers — and most other modern PWA APIs — will only run inside a secure context.
What is a secure context?
Section titled “What is a secure context?”A secure context is a Window or Worker whose origin was delivered over a secure transport. Browsers expose a straightforward boolean you can check at any time:
console.log(window.isSecureContext); // true or falseThe browser sets window.isSecureContext to true when:
- The page was loaded over
https://with a valid TLS certificate. - The page was loaded from
http://localhost,http://127.0.0.1, orhttp://[::1]— these loopback addresses are explicitly exempted by the spec and treated as secure by all modern browsers.
For any other http:// origin, window.isSecureContext is false and the service worker registration will throw a SecurityError or fail silently.
// Always guard registration with a secure-context checkif ('serviceWorker' in navigator) { if (!window.isSecureContext) { console.warn('Not a secure context — service worker will not register.'); } else { navigator.serviceWorker .register('/sw.js') .then((reg) => console.log('Registered, scope:', reg.scope)) .catch((err) => console.error('Registration failed:', err)); }}Mixed content blocks service worker registration
Section titled “Mixed content blocks service worker registration”A page is only considered secure if every resource it loads comes from a secure origin. Loading even a single http:// subresource from an https:// page creates mixed content, which browsers block or downgrade.
Mixed content matters for PWAs in two practical ways:
- Active mixed content (scripts, service workers, iframes over
http://) is always blocked. Yoursw.jsmust itself be served overhttps://. - Passive mixed content (images, audio, video over
http://) triggers a browser warning and, in some configurations, forceswindow.isSecureContexttofalse, preventing SW registration.
The fix is straightforward: audit all resource URLs in your app, migrate them to https://, and use Content Security Policy headers to enforce it going forward.
Service worker scope
Section titled “Service worker scope”The scope of a service worker determines which pages it controls. By default the scope is the directory that contains the SW file:
| SW file path | Default scope | Controls |
|---|---|---|
/sw.js | / | The entire origin |
/app/sw.js | /app/ | Only /app/ and below |
/shop/checkout/sw.js | /shop/checkout/ | Only /shop/checkout/ and below |
You can narrow the scope by passing a scope option to register(). You cannot widen it beyond the SW file’s directory — unless the server sends a Service-Worker-Allowed response header on the SW file with the desired wider scope:
Service-Worker-Allowed: /// Widen the scope with the header above set on /app/sw.jsnavigator.serviceWorker.register('/app/sw.js', { scope: '/' });Without that header, trying to register a wider scope than the SW file’s directory throws a SecurityError.
Localhost is secure — use it freely in development
Section titled “Localhost is secure — use it freely in development”During development you do not need a self-signed certificate or a tunnel. All modern browsers treat localhost, 127.0.0.1, and ::1 as secure origins, so you can register service workers, use the Push API, access the camera, and test the full installability flow on a plain http://localhost dev server.
// Full secure-context diagnostic you can drop into DevTools(function diagnose() { const results = { isSecureContext: window.isSecureContext, protocol: location.protocol, hostname: location.hostname, serviceWorkerSupported: 'serviceWorker' in navigator, }; console.table(results); if (!results.isSecureContext) { console.warn( 'Secure context required. Deploy over HTTPS or use localhost.' ); }})();