# Service worker cache hid releases for weeks

> A report traces three weeks of stale releases to a service worker cache, then documents the diagnosis and two-stage cleanup.

A service worker kept serving an old application release to some users for three weeks, according to a production account published on 2026-09-21. Deployments succeeded, the new code worked for the author, and neither the console nor the server showed an error. Affected browsers were serving cached HTML, JavaScript, and CSS instead of requesting the newly deployed files
([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

 The author first deployed a replacement service worker that activated immediately, deleted every cache, unregistered itself, and reloaded open pages. That recovery worker was then replaced with a minimal service worker that registers no `fetch` handler, stores nothing, and removes remaining caches during activation ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

<span id="what-the-report-found"></span>

## A deployed release behind an old cache

The application's service worker had been added so that the application could be installed on mobile. The report describes it as a small `sw.js` file, but its caching behavior reached the application's navigation document and its JavaScript and CSS. The server therefore had the new release while an affected browser already held the resources it continued to use ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

That distinction explains the conflicting observations recorded during the incident. The author could deploy a correction and see it working locally, while some users continued seeing an earlier version for days. The deployment status and server output described only what was available from the server; the installed worker determined that those browsers did not return to the network for the cached resources ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

Before identifying the worker, the author investigated the deployment, the hosting provider's cache, DNS propagation, and the application code. The report says the application was redeployed several times and that working code was changed because earlier corrections appeared not to have worked. None of those actions altered the browser-side cache that was serving the older application ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

The report attributes the persistence to two service-worker lifecycle details. It says the browser checks the `sw.js` file itself for an update, so leaving that file unchanged did not install a replacement worker. It also says that when a replacement did arrive, it waited by default while application tabs remained open. A user who kept a tab open could therefore remain under the earlier worker for an extended period ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

The W3C explainer independently describes the same lifecycle boundary: navigation within a worker's scope prompts a background update check, a byte-different worker script is installed as a new version, and the earlier version remains in control until the tabs using it are gone. The Service Workers specification defines a registration with installing, waiting, and active workers, and says `skipWaiting()` lets a worker move from waiting toward activation while clients are using the registration ([W3C service worker explainer](https://github.com/w3c/ServiceWorker/blob/main/explainer.md); [Service Workers specification](https://www.w3.org/TR/service-workers/)).

### The diagnostic path

The report gives two checks in Chrome DevTools. In Application, then Service Workers, a worker shown as `waiting` identifies a replacement that has not taken control. In Application, then Storage, clearing site data provides a second test: if the expected release appears after the stored data is removed, the cache was responsible for the stale view in this incident ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

Those checks separate the browser's retained state from another deployment attempt. The first exposes a lifecycle state, and the second changes the local state that the report had found to be decisive. In the author's case, seeing the correct version after clearing site data distinguished the service-worker cache from the deployment, hosting cache, DNS, and application-code theories already investigated ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

### The recovery worker

Deleting `sw.js` from the server was not the report's remedy. The author states that browsers with the old worker installed would retain it, so the first replacement was deliberately built as a kill switch. Its `install` listener calls `self.skipWaiting()`, and its `activate` listener waits for an asynchronous cleanup operation ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

That activation operation obtains the cache names with `caches.keys()`, deletes each cache with `caches.delete()`, unregisters the worker through `self.registration.unregister()`, and finds window clients with `self.clients.matchAll({ type: 'window' })`. It then navigates every returned client to its existing URL. These are the exact operations in the report's recovery example ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

The report explains the role of `skipWaiting()` in that sequence: it makes the new worker activate without waiting for all application tabs to close. Once active, the worker clears the caches, unregisters itself, and reloads the open pages. The sequence is intended to reach clients still controlled by the earlier installation, rather than relying on users to discover and remove their own site data ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

### The replacement after cleanup

The kill switch was a one-time recovery step. The author says installation still mattered for the application, so it was followed by a minimal service worker. This second worker also calls `skipWaiting()` on installation. During activation, it lists and deletes every cache, then calls `self.clients.claim()` ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

The minimal worker has no `fetch` listener. The resulting behavior is described as not intercepting requests and always allowing the browser to use the network. Its activation cleanup also removes any cache left from the earlier implementation. This design matches the author's stated requirement to retain the worker while ending the caching behavior that caused the incident ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

For an application that intentionally supports offline use, the report offers a different boundary. It says not to put `index.html` behind a cache-first strategy, to version cache names such as `cache-v2` and `cache-v3`, to delete old caches during activation, and to configure the host so that `sw.js` itself is not cached. These recommendations have not been independently reproduced ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

<span id="why-it-matters-for-pwa-developers"></span>

## Stale caches and their impact on deployed updates

The deployment and browser checks gave conflicting results. A successful deployment established that new files were present on the server, and a correct local result established that one browser could obtain them. Neither result established which responses a browser already controlled by the older service worker would return. The worker and its cache had to be inspected as a separate release surface ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

The report also shows why repeatedly changing application code was ineffective in this case. The affected clients were not requesting those changed resources from the server. Until the worker lifecycle advanced and the caches were cleared, another application deployment left the mechanism serving the older responses untouched. The recovery therefore targeted the installed worker before the author returned to a minimal steady-state worker ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

The recovery worker removes cached data and its own registration, then reloads clients. The steady-state worker remains registered, claims clients, and avoids request interception. The two examples serve different stages of the repair
([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

Inspect browser state before editing working code. The report's closing advice is to clear site data the next time a deployment appears not to work, before changing the application code. Within this incident, that action directly tested the cache hypothesis and exposed the current server release ([the report](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki)).

## Related entries

- [Capabilities](/reference/capabilities/): see catalog of web-platform capabilities.
- [Performance](/reference/performance/): see performance documentation.
- [Storage](/reference/storage/): see browser-storage documentation.

## Open and unverified

- The Chrome and operating-system versions used for the DevTools observations are not confirmed.
- Whether every affected user retained exactly the same cache contents is not confirmed.
- How frequently each affected browser checked `sw.js` is not confirmed.
- Whether any affected browser had already downloaded a replacement worker before the diagnosis is not confirmed.
- The hosting product and the cache-control headers returned for `sw.js`, `index.html`, JavaScript, and CSS are not confirmed.
- Whether the minimal service worker was required for installability in the application's target browsers is not confirmed.
- How the two replacement workers were sequenced across deployments, and how long each remained deployed, is not confirmed.
- Whether reloading each open client completed successfully on every affected device is not confirmed.

## Sources

- [Mon app servait une vieille version à mes utilisateurs pendant 3 semaines](https://dev.to/pando83/mon-app-servait-une-vieille-version-a-mes-utilisateurs-pendant-3-semaines-le-piege-du-service-5dki) (dev.to)
- [Service Workers specification](https://www.w3.org/TR/service-workers/) (W3C)
- [Service Worker explainer](https://github.com/w3c/ServiceWorker/blob/main/explainer.md) (W3C GitHub)