Tips & Tricks (Mis à jour: 02/06/2026)

Service Worker avec Claude Code : cache, mises à jour et offline

Guide pratique pour Service Worker avec Claude Code : cache, cycle de mise à jour, UX offline et exemples.

Service Worker avec Claude Code : cache, mises à jour et offline

Un Service Worker est très utile pour une PWA et une expérience offline, mais il devient vite dangereux s’il est ajouté sans règles. Si vous demandez à Claude Code “ajoute du cache” sans limites, l’application peut garder un vieux HTML, stocker des données privées ou refuser d’afficher une nouvelle version après déploiement.

Le plus simple est de voir le Service Worker comme un petit proxy entre le navigateur et le serveur. Il intercepte certaines requêtes et choisit entre le réseau, Cache API ou une page offline préparée. Ce guide couvre les décisions à prendre, un exemple copiable, l’invalidation de cache, le cycle de mise à jour, l’UX offline et les erreurs fréquentes.

Gardez les sources officielles à portée de main : MDN Service Worker API, le guide service workers de web.dev, les recommandations de cache web.dev et Chrome Workbox docs. Pour le contexte ClaudeCodeLab, lisez aussi le guide PWA avec Claude Code, les stratégies de cache et le guide IndexedDB.

Rôle d’un Service Worker

Un Service Worker est du JavaScript exécuté hors de la page. Il ne peut pas toucher le DOM, donc il ne modifie pas directement les boutons, formulaires ou états React. Il peut en revanche intercepter des requêtes éligibles et répondre avec le réseau, avec Cache API ou avec un fallback offline.

Le script de page disparaît quand l’onglet se ferme. Le Service Worker est réveillé par événements : install, activate, fetch, push et parfois la synchronisation en arrière-plan. Pour une première version solide, concentrez-vous sur fetch, Cache API et le cycle de mise à jour.

sequenceDiagram
  participant User as Utilisateur
  participant Page
  participant SW as Service Worker
  participant Cache as Cache API
  participant Net as Serveur
  User->>Page: Ouvre le site
  Page->>SW: Enregistre /sw.js
  Page->>SW: Lance une requête fetch
  SW->>Cache: Vérifie le cache
  alt En cache
    Cache-->>SW: Réponse stockée
  else Pas en cache
    SW->>Net: Demande la réponse récente
    Net-->>SW: Réponse fraîche
  end
  SW-->>Page: Réponse à afficher

Ce n’est donc pas une baguette magique de performance, mais un contrôleur de trafic. La qualité dépend de ce que vous stockez, de ce que vous supprimez, et de ce que l’utilisateur voit quand le réseau tombe.

Cas d’usage utiles

CasPourquoi c’est utileAttention
Documentation ou blogArticles, CSS, images et polices s’affichent plus vite au retourUn HTML trop longtemps en cache masque les corrections
Dashboard SaaSLa navigation reste visible sur réseau instableNe pas cacher facturation, compte ou données privées
Formulaire terrainL’utilisateur garde brouillons et actions en attente offlinePOST va dans IndexedDB, pas dans Cache API
Catalogue ecommerce ou médiaLes miniatures évitent des téléchargements répétésPrix, stock et images protégées demandent des règles de fraîcheur

Masa a testé ce modèle sur un petit site pédagogique. Cacher seulement images et polices a amélioré la deuxième visite. En revanche, mettre les pages d’article en Cache First a retardé la correction de fautes visibles. Pour Claude Code, il faut donc décrire la propriété et la durée de chaque cache, pas demander “cache tout”.

Brief Claude Code

Ajoutez les interdictions et la validation directement dans la demande.

Ajoute un Service Worker à l'app Vite existante.

Exigences:
- Place /sw.js dans public et utilise le scope /
- Cache seulement les assets statiques en GET
- Utilise Network First pour les navigations HTML
- Retourne /offline.html si la navigation échoue offline
- Ne cache pas API, POST, pages authentifiées ni autres origines
- Inclue une date ou version dans le nom du cache
- Supprime les anciens caches pendant activate
- Affiche une demande de rechargement si le nouveau worker est waiting

Validation:
- Vérifier Chrome DevTools > Application > Service Workers
- Passer Network en Offline et confirmer /offline.html
- Changer CACHE_VERSION et confirmer la suppression des vieux caches

Les exclusions sont essentielles. Une erreur sur API ou pages privées devient vite un bug de données, pas seulement une gêne d’interface.

Exemple minimal copiable

Placez ces quatre fichiers dans un dossier vide, par exemple sw-demo, puis lancez un serveur local. Un Service Worker exige HTTPS ou localhost; ouvrir le fichier HTML directement ne suffit pas.

python -m http.server 5173

Ouvrez http://localhost:5173.

<!-- index.html -->
<!doctype html>
<html lang="fr">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Service Worker Demo</title>
    <style>
      body {
        font-family: system-ui, sans-serif;
        margin: 2rem;
        line-height: 1.7;
      }
      button {
        padding: 0.7rem 1rem;
      }
    </style>
  </head>
  <body>
    <h1>Service Worker Demo</h1>
    <p id="status">En attente d'enregistrement.</p>
    <button type="button" onclick="location.reload()">Recharger</button>
    <script src="/register-sw.js"></script>
  </body>
</html>
// register-sw.js
const status = document.querySelector("#status");
let reloadRequested = false;
let updatePromptShown = false;

function setStatus(message) {
  if (status) status.textContent = message;
}

function askToReload(worker) {
  if (updatePromptShown) return;
  updatePromptShown = true;

  const ok = window.confirm(
    "Une nouvelle version est disponible. Recharger maintenant ?",
  );

  if (ok) {
    reloadRequested = true;
    worker.postMessage({ type: "SKIP_WAITING" });
  }
}

async function registerServiceWorker() {
  if (!("serviceWorker" in navigator)) {
    setStatus("Ce navigateur ne supporte pas les Service Workers.");
    return;
  }

  try {
    const registration = await navigator.serviceWorker.register("/sw.js", {
      scope: "/",
    });

    setStatus(`Service Worker enregistré: ${registration.scope}`);

    if (registration.waiting && navigator.serviceWorker.controller) {
      askToReload(registration.waiting);
    }

    registration.addEventListener("updatefound", () => {
      const worker = registration.installing;
      if (!worker) return;

      worker.addEventListener("statechange", () => {
        const hasOldController = Boolean(navigator.serviceWorker.controller);
        if (worker.state === "installed" && hasOldController) {
          askToReload(worker);
        }
      });
    });
  } catch (error) {
    console.error(error);
    setStatus("Échec de l'enregistrement du Service Worker.");
  }
}

navigator.serviceWorker?.addEventListener("controllerchange", () => {
  if (!reloadRequested) return;
  window.location.reload();
});

registerServiceWorker();
// sw.js
const CACHE_VERSION = "2026-06-02-v1";
const CACHE_PREFIX = "claude-sw-demo";
const CACHE_NAME = `${CACHE_PREFIX}-${CACHE_VERSION}`;

const APP_SHELL = [
  "/",
  "/index.html",
  "/offline.html",
  "/register-sw.js",
];

self.addEventListener("install", (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(APP_SHELL)),
  );
});

self.addEventListener("activate", (event) => {
  event.waitUntil(
    caches.keys().then((names) =>
      Promise.all(
        names
          .filter((name) => name.startsWith(CACHE_PREFIX))
          .filter((name) => name !== CACHE_NAME)
          .map((name) => caches.delete(name)),
      ),
    ),
  );
  self.clients.claim();
});

self.addEventListener("message", (event) => {
  if (event.data?.type === "SKIP_WAITING") {
    self.skipWaiting();
  }
});

self.addEventListener("fetch", (event) => {
  const { request } = event;
  if (request.method !== "GET") return;

  const url = new URL(request.url);
  if (url.origin !== self.location.origin) return;

  if (request.mode === "navigate") {
    event.respondWith(networkFirstNavigation(request));
    return;
  }

  if (["style", "script", "font", "image"].includes(request.destination)) {
    event.respondWith(staleWhileRevalidate(request));
  }
});

async function networkFirstNavigation(request) {
  const cache = await caches.open(CACHE_NAME);

  try {
    const response = await fetch(request);
    if (response.ok) cache.put(request, response.clone());
    return response;
  } catch {
    return (
      (await cache.match(request)) ||
      (await cache.match("/offline.html")) ||
      new Response("Offline", { status: 503 })
    );
  }
}

async function staleWhileRevalidate(request) {
  const cache = await caches.open(CACHE_NAME);
  const cached = await cache.match(request);

  const fetched = fetch(request)
    .then((response) => {
      if (response.ok) cache.put(request, response.clone());
      return response;
    })
    .catch(() => cached || new Response("Offline", { status: 503 }));

  return cached || fetched;
}
<!-- offline.html -->
<!doctype html>
<html lang="fr">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Vous êtes hors ligne</title>
  </head>
  <body>
    <main>
      <h1>Vous êtes hors ligne</h1>
      <p>Reconnectez-vous, puis rechargez cette page.</p>
      <button type="button" onclick="location.reload()">Réessayer</button>
    </main>
  </body>
</html>

L’exemple utilise Network First pour la navigation et Stale While Revalidate pour CSS, JavaScript, polices et images. Network First tente le serveur, puis revient au cache ou à offline.html. Stale While Revalidate affiche le cache immédiatement tout en rafraîchissant en arrière-plan. Ne l’appliquez pas sans réflexion aux actualités, prix, stocks ou écrans authentifiés.

Mise à jour et invalidation

Le cycle de mise à jour est le point le plus fragile. Quand sw.js change, le navigateur installe un nouveau worker. Si une ancienne page reste ouverte, ce worker peut rester en waiting. Le code d’enregistrement détecte cet état, demande confirmation à l’utilisateur et envoie SKIP_WAITING seulement en cas d’accord.

Le worker appelle ensuite self.skipWaiting(), s’active, supprime les anciens caches et réclame les clients. Sans cette séquence, un utilisateur peut garder un vieux app-cache-v1 longtemps après un correctif.

Nommez les caches avec une date, un numéro de release ou un commit. Si votre build produit des fichiers hashés, synchronisez la liste de precache avec la sortie du build. Quand une liste manuelle devient fragile, Workbox est utile, mais il ne décide pas quelles données métier sont sûres à cacher.

UX offline

Le support offline ne s’arrête pas à une réponse Cache API. L’utilisateur doit savoir si son travail est enregistré, en attente de synchronisation ou en échec. Pour les formulaires, ne stockez pas les POST dans Cache API. Enregistrez les brouillons ou jobs dans IndexedDB côté page, puis réessayez quand le navigateur revient online. Background Sync peut aider, mais son support varie; prévoyez aussi l’événement online et un bouton visible de reprise.

Dans la demande à Claude Code, précisez le texte offline, le bouton de retry, les états de brouillon et les messages d’échec. Une application terrain devrait au minimum distinguer “envoyé”, “enregistré sur cet appareil” et “échec de synchronisation”.

Erreurs fréquentes

La première erreur est le scope. Un worker placé en /app/sw.js contrôle /app/ par défaut, pas tout le site. Pour contrôler tout le site, placez-le en /sw.js et utilisez le scope /.

La deuxième erreur est une URL 404 dans cache.addAll(). Une seule entrée manquante fait échouer tout l’install. Après un changement de Claude Code, vérifiez l’état dans DevTools Application et Cache Storage.

La troisième erreur est le cache de données privées. Ne cachez pas /api/me, pages de facturation, HTML admin ou JSON spécifique à l’utilisateur sans stratégie explicite de suppression. Le cache navigateur reste un stockage sur l’appareil.

La quatrième erreur est l’absence d’UX de mise à jour. De vieux workers peuvent garder de vieux JS et CSS. Versionnez les caches, supprimez pendant activate et laissez l’utilisateur recharger lorsqu’un worker attend.

Enfin, un Service Worker n’est pas un stockage permanent. Le navigateur peut évincer les caches. Les réponses opaque cross-origin sont difficiles à mesurer. Le DOM n’est pas accessible. Cela fonctionne seulement en HTTPS ou localhost. Ne promettez pas un offline complet sans test du flux réel.

Résumé et CTA

Un Service Worker améliore les revisites, l’offline et la qualité PWA. La voie sûre consiste à décider la propriété des caches, le cycle de mise à jour, les règles de données privées et les écrans offline avant de laisser Claude Code modifier les fichiers.

ClaudeCodeLab accompagne les conversions PWA, la conception de cache, les formulaires offline, les migrations Workbox et les revues d’implémentation Claude Code. Pour rendre un site plus rapide sans servir de données anciennes ou privées, démarrez par la formation et consultation Claude Code.

Testée dans Chrome local, cette configuration crée claude-sw-demo-2026-06-02-v1 dans Application après le premier chargement. En passant Network sur Offline puis en rechargeant, offline.html s’affiche. En modifiant CACHE_VERSION, les anciens caches sont supprimés pendant activate, ce qui en fait une bonne base de vérification.

#Claude Code #Service Worker #PWA #offline #caching
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.