Web reliability

Production readiness for progressive web apps

A service worker changes the browser from a passive client into a programmable cache. That can improve resilience, but it also creates a second delivery system that must be designed, tested, and recoverable.

Published 13 August 20268 minute readReviewed by Shago engineering

Define what “offline” means

Offline support is not one feature. A product may offer a branded offline page, read-only access to previously viewed records, queued writes, or a complete local workflow. Write down the promised behavior for each route and action. Do not display a successful state for a write that has only been stored locally; label it as pending and show how the user can retry or cancel.

Classify data by freshness. A logo can remain cached for months when its URL is content-addressed. Account balance, access rights, and incident status may require the network. The cache strategy should follow the consequence of stale data rather than file type alone.

Keep navigation fallback narrow

Single-page applications often return index.html for unknown navigations. A service worker can repeat that behavior offline, but a broad fallback may replace real files such as /ads.txt, /robots.txt, manifests, downloads, or server-rendered articles with the application shell.

Allow only routes owned by the client router, and denylist infrastructure and content paths. Test direct navigation with and without an installed service worker. Also verify that a genuinely missing asset returns 404 instead of a convincing HTML response with status 200.

Useful test: compare the response body and content type in a clean browser, a returning browser, an incognito session, and a command-line HTTP client.

Choose caching strategies by risk

ResourceTypical strategyMain risk
Hashed JS and CSSCache firstOrphaned old bundles
Page navigationNetwork first with bounded timeoutStale application shell
Public editorial contentStale while revalidateDelayed corrections
Authenticated APIUsually network onlyPrivate data leakage or stale decisions

Cache keys must account for the inputs that change a response. Be cautious with cookies, authorization headers, locale, tenant, and query parameters. Never place personalized responses into a shared cache without an explicit isolation design.

Make updates understandable

A new worker installs separately from the worker controlling the current page. Forcing immediate activation can make the open page run old JavaScript against new cached assets. Waiting forever leaves users on vulnerable or broken releases. Choose an update policy and make it visible.

For ordinary releases, notify the user that an update is available and reload after consent or at a safe navigation boundary. For critical releases, immediate activation may be appropriate, but test in-progress forms and queued operations. Ensure only one reload occurs and that the newly activated worker takes control.

Plan an escape hatch

A bad service worker can keep serving itself after the server is fixed. Maintain a recovery release that unregisters obsolete workers, clears only caches owned by the application, and reloads once. Avoid generic cache deletion because other applications on the same origin may use Cache Storage.

Serve the worker script with revalidation or no-cache semantics. Version cache names, remove outdated versions during activation, and keep deletion logic compatible with at least the previous deployed worker.

Test real failure modes

Browser “offline mode” is useful but incomplete. Test slow connections, DNS failure, request timeout, partial API outage, expired sessions, quota exhaustion, corrupted cached data, and a deployment where HTML references assets that have already been removed. Confirm that keyboard navigation, focus order, labels, contrast, and reduced motion still work in offline and update prompts.

Use a fresh profile for first-install behavior and a persistent profile for upgrade behavior. Automated audits help, but release acceptance should include manual checks on actual mobile devices where process eviction and storage pressure are common.

Measure the experience

Track worker installation failure, cache errors, offline page views, update adoption time, queued operation age, and recovery success. Do not collect full URLs or payloads when they may contain personal information. A PWA is ready when the team can detect a bad release, understand its blast radius, and recover users who already cached it.