Website-Pflichtencheckby Jurono
WebsiteTechnicalMaintenancePerformanceCode

Deployment Is Live, Browser Is Old: Service Worker Updates Without Version Chaos

A new release is online, but open tabs can keep old code. Audit the service-worker lifecycle, cache versions, skipWaiting, and mixed-version deployments.

By Jurono
Updated: August 12, 2026

A deployment succeeds. CI is green, the CDN serves the new build, and the bug is gone in a fresh browser.

Some users still report the old bug. Others suddenly see a blank screen when they open a rarely used part of the application. Support gets the worst possible reproduction note: “It works for us.”

When a website or PWA uses a service worker, that can be completely consistent with the platform. “The new version is deployed” does not automatically mean “every open browser is already running that same version.”

Service workers have their own lifecycle. Existing tabs can remain controlled by the active worker while a newer worker has already installed and is waiting. Cache Storage can still contain resources from earlier releases. An aggressive immediate takeover can create the opposite problem: a new service worker starts controlling a document whose JavaScript still belongs to the previous release.

This is not obscure PWA trivia. It is release engineering inside the browser.

Why a browser can remain old after deployment

When the browser discovers that the service-worker script changed, it does not immediately replace the worker that is already active. The new version installs first. If pages are still controlled by the existing worker, the new worker normally enters the waiting state.

It becomes active once the previous worker controls no clients.

That conservative behavior is intentional. An already loaded application should not have its network layer replaced halfway through a session. It protects against a particularly awkward mixed state: the HTML and JavaScript in the tab belong to release A, while network requests are suddenly handled by service worker B.

The consequence is equally important: a user who leaves a dashboard open for days may remain on an older client version longer than the deployment team expects.

The real risk is not “old cache” but mixed versions

“Clear the cache” is a popular universal answer. It skips the more useful question: Which versions are allowed to exist at the same time, and are they compatible with each other?

A realistic post-deployment state can look like this:

  • The server and API already run version B.
  • An open tab still contains HTML and JavaScript from version A.
  • Service worker A controls that tab.
  • Service worker B is installed and waiting.
  • Cache A contains older static resources.
  • Cache B was prepared during installation.
  • A second fresh browser may already observe a different state.

The system needs to survive that transition.

The tempting shortcut: skipWaiting()

Calling self.skipWaiting() lets a newly installed service worker bypass the waiting phase and activate sooner.

That can be appropriate, but it is not a free “always use the latest version” switch. Chrome and web.dev explicitly warn about mixed-version behavior: the new worker can begin controlling pages that were loaded under the previous worker.

A common failure looks like this.

Release A initially loads app-A.js. A rarely used dialog is loaded later through a dynamic import from dialog-A.js.

Release B is deployed. The new worker activates immediately and old assets are deleted at the same time.

The user now opens the dialog in the still-running release-A tab. The page requests dialog-A.js, but the new worker or the CDN only knows about dialog-B.js.

The result is a chunk error, blank area, or reload loop even though release B itself is healthy.

Immediate activation is therefore a product decision about version compatibility, not merely a service-worker line.

A safer update model

For many business websites and SaaS interfaces, a controlled transition is more reliable than forced immediate takeover.

Detect the new version

A registration exposes states such as installing, waiting, and active. The updatefound event can tell the page that a new worker is being installed. For long-lived applications, registration.update() can deliberately trigger an update check.

That matters for dashboards, point-of-sale interfaces, admin tools, and SaaS products that may not be fully closed every day.

Wait for a safe upgrade point

When a new worker is waiting, the application can display a small message: “A new version is available. Update now.”

The user can finish a form, upload, or editing session first. The application can then activate the new version and perform one controlled reload. For applications with unsaved work, that is usually better than surprising the user with a refresh mid-task.

Observe activation deliberately

controllerchange signals that the controlling service worker changed. It can be a useful point to reload after an intentional upgrade, but only once and only when the application actually requested the switch. Otherwise, update code can easily produce reload loops or hard-to-reproduce state changes.

Cache versioning belongs to the release process

Cache Storage manages named caches. Old caches do not disappear simply because another deployment exists.

A common maintenance model is therefore to:

  • bind cache names to a release or schema version,
  • prepare required resources during install,
  • remove clearly obsolete cache versions during activate,
  • separate runtime caches from static precache resources,
  • avoid caching data whose freshness rules are undefined.

Timing matters. If an application combines skipWaiting() with immediate deletion of every previous asset, still-open old documents may break. When the normal waiting behavior is respected, activation provides a cleaner boundary: the previous worker no longer controls clients.

Do not version the service-worker URL for every release

It can seem neat to rename the worker itself on every deployment: /sw.js becomes /sw-v42.js.

web.dev advises against treating the registered service-worker URL as the release version. Older cached HTML can continue registering the old URL. That can leave multiple registrations or clients tied to an update path that the team no longer maintains.

A stable registration path such as /sw.js is easier to reason about. Its contents change; the browser detects the difference and runs the intended update lifecycle. Release identifiers belong in cache names, manifests, or build metadata rather than necessarily in the identity of the registration.

Deployments need an asset transition strategy

The service worker is only half of the problem. The CDN or hosting layer is the other half.

If release B immediately deletes all hashed resources from release A, an older tab can fail later even without a service worker when it asks for a chunk that was not loaded earlier. A resilient deployment therefore keeps previous immutable build artifacts available for a reasonable transition window. Hashed assets can coexist because the filename uniquely represents the content.

That reduces the need to force every open client onto the same release at the exact moment of deployment.

The API must survive the transition too

The most damaging version mismatch is often not JavaScript versus JavaScript. It is client A versus API B.

A backend release removes a field, renames an endpoint, or changes validation. Old browser tabs still send requests according to the previous contract. That leads to a practical release rule: frontend and API changes should remain backward-compatible for at least the realistic client-update window.

When a breaking change is unavoidable, the product needs an explicit version boundary, an upgrade gate, or a controlled “please reload” path. Otherwise a service-worker lifecycle issue becomes a workflow or data-integrity issue.

The release test that happy-path QA rarely runs

A meaningful update test deliberately starts on the old version.

  1. Open version A and leave a tab active.
  2. Open a second tab and let both be controlled by the same worker.
  3. Deploy version B.
  4. Confirm that B is detected and becomes visible as waiting.
  5. In the old tab, exercise navigation, dynamic imports, forms, and API actions.
  6. Close one tab and verify that the other remains stable.
  7. Trigger the upgrade deliberately.
  8. Verify that controllerchange, reload behavior, and preserved user state work together.
  9. Test offline and slow-network conditions.
  10. Simulate a broken new service worker and verify that the current worker remains usable.
  11. Inspect Cache Storage after several releases.
  12. Revisit a long-lived tab hours or days later.

This does more than prove that the PWA is “installable.” It proves that releases survive real browser lifecycles.

Symptoms that point to an update problem

A few patterns deserve immediate suspicion:

  • A bug disappears only after a hard reload.
  • Some users see the new interface while others stay on the old one.
  • Dynamic imports fail with chunk 404s after deployments.
  • Reloading suddenly fixes API or form errors.
  • Support can reproduce the issue only in long-lived tabs.
  • Cache Storage contains many old versions that are never removed.
  • The application registers multiple service-worker paths for the same product area.
  • An update prompt refreshes without protecting unsaved work.
  • Every release eventually leads to “delete site data.”

That last one matters: “clear your cache” is an emergency workaround, not an update architecture.

What Website-Pflichtencheck would inspect

A technical service-worker and release review can examine:

  • registration, scope, and a stable worker path,
  • update detection and registration.update(),
  • installing, waiting, and active states,
  • use of skipWaiting() and clients.claim(),
  • behavior with multiple open tabs,
  • controllerchange and reload logic,
  • Cache Storage versioning and cleanup,
  • caching strategy for HTML, static assets, and runtime data,
  • retention of previous hashed assets after deployments,
  • dynamic imports and chunk failures,
  • compatibility between old clients and the new API,
  • offline fallbacks,
  • behavior after a failed service-worker installation,
  • update prompts and protection of unsaved user work,
  • regression tests spanning at least two release versions.

The goal is not to avoid service workers. Used well, they provide offline capabilities, controlled caching, and fast repeat visits. But they make the browser part of the release infrastructure.

If a deployment is reliable only when every user immediately closes every tab and clears site data, the user is not “staying on the page too long.” The update path is incomplete.

Jurono logo

Jurono

Technical website audits, website fixes, and AI code rescue for small businesses, practices, law firms, and founders in Germany.

Get our free security checklist before you go.

Download free PDF

Want a first signal in 30 seconds? Run the free website quick test.

Get website notes by email

One short technical note every two weeks. No spam, no sales pitch.

Matching offers

Move forward directly

Based on the topics in this article — without a long search.

Manual Website Check

When nobody is sure which scripts, cookie signals, or technical risks are currently running on the site.

249

Manual technical first assessment and clear priorities within two business days.

  • Quickly see whether tracking, cookies, external services, or HTTPS look suspicious
  • Mobile, load time, and technical issues explained in plain language
  • The most important points in a short priority list
Get clarity with Manual Website Check

Technical Website Audit

When the website matters, but nobody knows which visible required areas, technical risks, and fixes actually have priority.

549

Audit, assessment, and concrete action plan within 3-5 business days.

  • Everything from the manual website check, assessed and documented in more depth
  • Concrete findings for cookie, tracking, and external service signals
  • Visible required areas checked technically, without legal advice
Request Technical Website Audit

AI Code Triage

When the project starts, but nobody knows why it keeps breaking.

390

Code review, build/import check, and rescue plan within two business days.

  • Repository check for broken imports, missing packages, and build errors
  • Assessment: repair, restructure, or discard
  • Prioritized fix list with effort estimate
Request AI Code Triage

Get clarity before you commit to fixes.

Start with a technical check. If the findings are minor, you can stop there, hand the report to your existing team, or book targeted fixes later.

Technical audit and implementation, not legal advice. I check visible signals, integrations, and delivery issues; legal texts and binding legal assessments remain the work of lawyers or privacy consultants.

Deployment Is Live, Browser Is Old: Service Worker Updates Without Version Chaos