Skip to content

Secure a PWA

Published Updated

A PWA uses a secure context for powerful APIs, a Content Security Policy (CSP) for resource loading, and an intentional service worker scope for request control. Configure and check these boundaries during deployment.

Use window.isSecureContext to check the active context and test for serviceWorker in navigator before registration. CSP violations can be observed in the document with ReportingObserver or sent to an endpoint configured with reporting headers and directives (Secure contexts, MDN; Content-Security-Policy, MDN).

MDN describes ServiceWorkerContainer.register() as widely available across browsers since April 2018 (ServiceWorkerContainer.register(), MDN). MDN marks the CSP response header as widely available, although support for individual directives varies (Content-Security-Policy, MDN).

MDN defines potentially trustworthy origins to include HTTPS and local development origins such as http://localhost. Features restricted to secure contexts are unavailable when the active document is not trustworthy. Redirect production HTTP traffic to HTTPS, and check the runtime property before starting a feature that requires a secure context.

The fallback keeps navigation and online content available while skipping service worker registration.

const status = document.createElement('output');
status.id = 'offline-status';
status.setAttribute('aria-live', 'polite');
document.body.append(status);
if (!window.isSecureContext) {
status.textContent = 'Offline installation is unavailable on this connection.';
} else if (!('serviceWorker' in navigator)) {
status.textContent = 'This browser runs the online experience.';
} else {
navigator.serviceWorker.register('/sw.js', { scope: '/' })
.then(() => { status.textContent = 'Service worker registration was created or updated.'; })
.catch(() => { status.textContent = 'The online experience remains available.'; });
}

The explicit branches make a failed security or capability check visible without blocking the page.

The Content-Security-Policy response header lets a site restrict resources such as scripts, styles, images, frames, and connections. MDN’s report-only example uses Content-Security-Policy-Report-Only during testing to report violations without blocking execution.

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'

This example limits default and script loads to the same origin, blocks plugins, restricts <base> URLs, and prevents embedding. Adapt the policy to resources the application actually loads.

The default registration scope is the directory containing the service worker script. To request a broader scope than that directory, the service worker script response must supply a Service-Worker-Allowed header that permits it (ServiceWorkerContainer.register(), MDN; Service worker script response, W3C).

Place /app/sw.js under /app/ and register it with { scope: '/app/' } when it should control only that path. Avoid overlapping registrations: MDN warns that overlapping scopes are generally safer to avoid because only one registration controls a document.

Inspect the final response, not only source files. Confirm that the page is a secure context, the CSP header is present on the document response, and registration.scope matches the intended URL boundary. Observe CSP violations with ReportingObserver, or send reports to an endpoint configured by the policy’s reporting directives (Content-Security-Policy, MDN).