OUJOOD.COM
Qu'est-ce qu'un Service Worker ?
HTML5 JavaScript – mise à jour 2026Un Service Worker est un script JavaScript qui s'exécute en arrière-plan, séparé de la page web, sans accès au DOM. Concrètement, il se place entre le navigateur et le réseau : toutes les requêtes HTTP de votre page passent d'abord par lui, et il décide quoi faire — répondre depuis le cache, transmettre au serveur, ou renvoyer une page de secours.
C'est la technologie sur laquelle reposent les Progressive Web Apps (PWA) depuis 2017. Elle remplace définitivement l'AppCache, déprécié et retiré des navigateurs modernes. En 2026, les Service Workers sont supportés par tous les navigateurs majeurs (Chrome, Firefox, Safari, Edge) et fonctionnent aussi sur mobile.
Trois choses qu'un Service Worker permet de faire et qu'AppCache ne permettait pas :
- Contrôle total sur le cache : vous décidez quand mettre en cache, quoi mettre en cache, et quand supprimer.
- Interception des requêtes : vous pouvez modifier une requête, lui substituer une réponse en cache, ou combiner les deux.
- Fonctionnement en arrière-plan : synchronisation différée, notifications push — même quand la page est fermée.
Une contrainte importante : les Service Workers ne fonctionnent qu'en HTTPS (ou sur localhost pour le développement). C'est une mesure de sécurité — un script qui intercepte tout le trafic réseau ne peut pas tourner sur une connexion non chiffrée.
Enregistrer un Service Worker
Tout commence dans votre page HTML principale. On enregistre le Service Worker avec navigator.serviceWorker.register(). Cette ligne crée un fichier service-worker.js à la racine de votre site, que le navigateur télécharge et installe en arrière-plan.
La portée (scope) d'un Service Worker dépend de son emplacement : un fichier à la racine contrôle toutes les pages du site, un fichier dans /app/ ne contrôle que les pages sous ce chemin.
// À placer dans votre page HTML principale (index.html ou équivalent) if ('serviceWorker' in navigator) { // Vérification de la compatibilité navigateur avant tout window.addEventListener('load', function() { navigator.serviceWorker.register('/service-worker.js') .then(function(registration) { console.log('Service Worker enregistré, portée :', registration.scope); }) .catch(function(error) { console.log('Échec de l\'enregistrement :', error); }); }); }
Le if ('serviceWorker' in navigator) est indispensable : il empêche les erreurs sur les vieux navigateurs qui ne connaissent pas cette API. L'enregistrement est lancé après le chargement complet de la page (load) pour ne pas ralentir l'affichage initial.
Le cycle de vie d'un Service Worker
Un Service Worker passe par trois états successifs. Comprendre ce cycle évite les bugs les plus courants, notamment les situations où le cache semble ne pas se mettre à jour.
- Installation (
install) : le Service Worker est téléchargé et installé. C'est ici qu'on pré-cache les ressources statiques. Si une erreur survient, l'installation échoue et le Service Worker n'est pas activé. - Activation (
activate) : le Service Worker prend le contrôle. C'est ici qu'on nettoie les anciens caches. L'activation n'a lieu que quand aucun onglet n'utilise encore l'ancienne version. - Interception (
fetch) : le Service Worker est actif et intercepte les requêtes réseau. C'est le cœur de la logique de cache.
// service-worker.js const CACHE_NOM = 'mon-app-v1'; // Événement install : pré-cache les fichiers indispensables au premier lancement self.addEventListener('install', function(event) { event.waitUntil( caches.open(CACHE_NOM).then(function(cache) { return cache.addAll([ '/', '/style.css', '/app.js', '/offline.html' ]); }) ); }); // Événement activate : supprime les anciens caches devenus inutiles self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames .filter(function(name) { return name !== CACHE_NOM; }) .map(function(name) { return caches.delete(name); }) ); }) ); });
event.waitUntil() est essentiel : il demande au navigateur d'attendre que la promesse se résolve avant de passer à l'étape suivante. Sans lui, l'installation pourrait se terminer avant que les fichiers soient réellement en cache.
Intercepter les requêtes avec l'événement fetch
L'événement fetch se déclenche à chaque requête HTTP émise par la page. C'est là qu'on choisit une stratégie de cache. La plus simple est cache-first : on cherche d'abord dans le cache, et on va sur le réseau seulement si la ressource est absente.
// Stratégie cache-first : idéale pour les ressources statiques (CSS, JS, images) self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request).then(function(reponseCache) { if (reponseCache) { return reponseCache; // Ressource trouvée en cache : on la retourne directement } // Absente du cache : on va chercher sur le réseau return fetch(event.request).catch(function() { // Hors ligne et ressource absente : page de secours return caches.match('/offline.html'); }); }) ); });
Stratégie network-first
Pour les pages dynamiques ou les données API, on préfère network-first : on essaie d'abord le réseau pour avoir les données les plus récentes, et on bascule sur le cache si la connexion échoue. C'est le bon choix pour un flux d'actualités ou une liste de produits.
// Stratégie network-first : réseau en priorité, cache en secours self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request) .then(function(reponseReseau) { // Requête réseau réussie : on met à jour le cache pour la prochaine fois var clone = reponseReseau.clone(); caches.open(CACHE_NOM).then(function(cache) { cache.put(event.request, clone); }); return reponseReseau; }) .catch(function() { // Réseau inaccessible : on tente le cache, sinon page offline return caches.match(event.request) .then(function(reponseCache) { return reponseCache || caches.match('/offline.html'); }); }) ); });
Notez reponseReseau.clone() : une réponse HTTP ne peut être lue qu'une seule fois. On clone la réponse pour pouvoir l'envoyer à la page ET la stocker en cache simultanément.
Mettre en cache dynamiquement avec l'API Cache Storage
Le pré-cache dans install couvre les ressources connues à l'avance. Pour les ressources découvertes à la navigation (images d'articles, pages visitées), on les ajoute au cache au fil de l'eau depuis l'événement fetch.
self.addEventListener('fetch', function(event) {
// On ne met en cache que les requêtes GET (pas les POST ou DELETE)
if (event.request.method !== 'GET') return;
event.respondWith(
caches.match(event.request).then(function(reponseCache) {
if (reponseCache) return reponseCache;
return fetch(event.request).then(function(reponseReseau) {
// On vérifie que la réponse est valide avant de la mettre en cache
if (!reponseReseau || reponseReseau.status !== 200) {
return reponseReseau;
}
var clone = reponseReseau.clone();
caches.open(CACHE_NOM).then(function(cache) {
cache.put(event.request, clone); // Ajout dynamique dans le cache
});
return reponseReseau;
});
})
);
});
Le filtre reponseReseau.status !== 200 est important : mettre en cache une réponse 404 ou 500 serait contre-productif — l'erreur serait servie indéfiniment depuis le cache.
Forcer la mise à jour du Service Worker
Le navigateur télécharge un nouveau Service Worker dès qu'il détecte un changement dans le fichier service-worker.js — même d'un seul octet. Mais le nouveau Service Worker attend que tous les onglets utilisant l'ancienne version soient fermés avant de s'activer.
Pour forcer l'activation immédiate dès l'installation, on utilise self.skipWaiting() dans install et clients.claim() dans activate. À utiliser avec prudence sur les applications critiques.
self.addEventListener('install', function(event) {
self.skipWaiting(); // Ne pas attendre la fermeture des onglets
event.waitUntil(
caches.open(CACHE_NOM).then(function(cache) {
return cache.addAll(['/', '/style.css', '/app.js', '/offline.html']);
})
);
});
self.addEventListener('activate', function(event) {
event.waitUntil(
clients.claim().then(function() { // Prendre le contrôle de tous les onglets ouverts
return caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames
.filter(function(name) { return name !== CACHE_NOM; })
.map(function(name) { return caches.delete(name); })
);
});
})
);
});
La bonne pratique pour gérer les versions : changer le nom du cache (mon-app-v1 → mon-app-v2) à chaque déploiement. Le nettoyage dans activate supprimera automatiquement l'ancienne version.
Exemple complet : Service Worker pour une PWA simple
Voici un Service Worker de production minimal, combinant pré-cache, cache dynamique, stratégie réseau adaptée et page offline. C'est un bon point de départ pour n'importe quelle application web qui doit fonctionner hors connexion.
// service-worker.js — version complète const CACHE_NOM = 'pwa-app-v1'; const RESSOURCES_STATIQUES = ['/', '/style.css', '/app.js', '/offline.html']; self.addEventListener('install', function(event) { self.skipWaiting(); event.waitUntil( caches.open(CACHE_NOM).then(function(cache) { return cache.addAll(RESSOURCES_STATIQUES); }) ); }); self.addEventListener('activate', function(event) { event.waitUntil( clients.claim().then(function() { return caches.keys().then(function(noms) { return Promise.all( noms.filter(function(n) { return n !== CACHE_NOM; }) .map(function(n) { return caches.delete(n); }) ); }); }) ); }); self.addEventListener('fetch', function(event) { if (event.request.method !== 'GET') return; var url = new URL(event.request.url); // Ressources statiques : cache-first if (RESSOURCES_STATIQUES.includes(url.pathname)) { event.respondWith( caches.match(event.request).then(function(r) { return r || fetch(event.request); }) ); return; } // Tout le reste : network-first avec mise en cache dynamique event.respondWith( fetch(event.request) .then(function(reponse) { if (reponse && reponse.status === 200) { var clone = reponse.clone(); caches.open(CACHE_NOM).then(function(cache) { cache.put(event.request, clone); }); } return reponse; }) .catch(function() { return caches.match(event.request) .then(function(r) { return r || caches.match('/offline.html'); }); }) ); });
Déboguer un Service Worker dans le navigateur
Chrome DevTools (et Firefox) proposent un panneau dédié sous Application > Service Workers. Vous y voyez l'état du Service Worker (installing, waiting, active), pouvez forcer une mise à jour, simuler le mode hors ligne, et inspecter le contenu du cache dans Application > Cache Storage.
Pendant le développement, cochez Update on reload dans DevTools : le Service Worker est réinstallé à chaque rechargement de page, ce qui évite les surprises liées au cache.
Compatibilité et support en 2026
Les Service Workers sont supportés par tous les navigateurs modernes depuis plusieurs années. Safari a comblé ses lacunes depuis la version 16.4 (2023) — les notifications push et la synchronisation en arrière-plan fonctionnent désormais sur iOS. Internet Explorer ne supporte pas les Service Workers, mais il ne représente plus un usage significatif en 2026.
Remarque : Un Service Worker ne peut contrôler que les pages de la même origine. Impossible d'intercepter les requêtes vers un autre domaine depuis un Service Worker d'un domaine différent.
Par carabde: le 13 avril 2026