Cache HTTP : cache fort, cache négocié et stratégies CDN
Cache HTTP : la première ligne de défense de l'optimisation des performances
Une configuration de cache appropriée permet zéro requête réseau au deuxième accès, réduisant le temps de chargement de secondes à millisecondes.
| Type de cache | Mécanisme | Requête au serveur ? | Vitesse |
|---|---|---|---|
| Cache fort | Cache-Control / Expires | Non | La plus rapide (0ms) |
| Cache négocié | ETag / Last-Modified | Oui (requête conditionnelle) | Rapide (réponse 304) |
| Pas de cache | no-cache / no-store | Oui (requête complète) | La plus lente |
I. Cache fort
Directives Cache-Control
Cache-Control: max-age=31536000, immutable
| Directive | Fonction | Scénario applicable |
|---|---|---|
max-age=N |
Mettre en cache N secondes | Ressources statiques |
s-maxage=N |
CDN met en cache N secondes (remplace max-age) | Scénarios CDN |
immutable |
Ne pas revalider même si expiré | Noms de fichiers avec hachage |
public |
Autoriser le cache intermédiaire | CDN |
private |
Cache du navigateur uniquement | Contenu personnalisé |
no-cache |
Doit valider avant chaque utilisation | Pages HTML |
no-store |
Ne rien mettre en cache | Données sensibles |
must-revalidate |
Doit valider après expiration | Réponses API |
stale-while-revalidate=N |
Retourner l'ancienne valeur après expiration, actualiser en arrière-plan | Mise à jour progressive |
Expires (ancien standard)
Expires: Thu, 02 Jun 2027 00:00:00 GMT
Priorité : Cache-Control: max-age > Expires.
II. Cache négocié
ETag / If-None-Match
# Première requête
GET /api/data
→ 200 OK
ETag: "abc123"
Cache-Control: max-age=0
# Requête suivante (conditionnelle)
GET /api/data
If-None-Match: "abc123"
→ 304 Not Modified (sans corps de réponse, économise la bande passante)
Last-Modified / If-Modified-Since
# Première requête
GET /style.css
→ 200 OK
Last-Modified: Mon, 01 Jun 2026 12:00:00 GMT
# Requête suivante
GET /style.css
If-Modified-Since: Mon, 01 Jun 2026 12:00:00 GMT
→ 304 Not Modified
ETag vs Last-Modified
| Dimension | ETag | Last-Modified |
|---|---|---|
| Précision | Niveau octet | Niveau seconde |
| Performance | Nécessite un calcul de hachage | Lecture directe de l'heure du fichier |
| Fiabilité | Élevée | Faible (modifications multiples en 1 seconde perdues) |
| Distribué | Doit garantir un hachage cohérent | Naturellement cohérent |
Recommandation : Prioriser ETag.
III. Matrice des stratégies de cache
Pages HTML
Cache-Control: no-cache
HTML est le fichier d'entrée, doit être validé à chaque fois pour s'assurer que les utilisateurs obtiennent la dernière version.
Ressources statiques avec hachage
Cache-Control: max-age=31536000, immutable
Le nom du fichier contient un hachage (ex., app.abc123.js), quand le contenu change, le hachage change → l'URL change → nouvelle requête. L'ancienne URL ne change jamais, peut être mise en cache un an et marquée immutable.
Ressources statiques sans hachage
Cache-Control: max-age=86400, stale-while-revalidate=604800
Après expiration, retourne d'abord l'ancienne valeur, actualise silencieusement en arrière-plan, imperceptible pour l'utilisateur.
Réponses API
Cache-Control: private, max-age=0, must-revalidate
Les réponses API contiennent généralement des données utilisateur, private interdit le cache CDN, must-revalidate garantit qu'aucune valeur ancienne n'est retournée après expiration.
IV. En-tête Vary : extension de la clé de cache
Vary: Accept-Encoding, Accept-Language
Vary indique au cache : en plus de l'URL, distinguer les versions de cache selon les valeurs de ces en-têtes de requête.
| Valeur Vary | Signification | Scénario |
|---|---|---|
Accept-Encoding |
Cacher par méthode de compression | gzip vs br |
Accept-Language |
Cacher par langue | Sites multilingues |
Cookie |
Cacher par Cookie | Pages personnalisées |
* |
Chaque requête avec cache indépendant | Équivaut à pas de cache |
Attention : Vary: Cookie entraîne un taux de réussite de cache très faible, à utiliser avec précaution.
V. Stratégie de cache CDN
Contrôle de récupération d'origine
# CDN met en cache 1 jour, navigateur met en cache 5 minutes
Cache-Control: public, s-maxage=86400, max-age=300
| Scénario | s-maxage | max-age | Description |
|---|---|---|---|
| Ressources statiques | 31536000 | 31536000 | CDN et navigateur avec cache long |
| Réponses API | 300 | 0 | CDN met en cache 5 minutes, navigateur ne met pas en cache |
| HTML | 60 | 0 | CDN met en cache 1 minute, navigateur ne met pas en cache |
| Contenu personnalisé | — | 0 | CDN ne met pas en cache, navigateur ne met pas en cache |
Actualisation du cache CDN
| Méthode | Vitesse | Applicable |
|---|---|---|
| Préchauffage d'URL | Rapide | Nouveau contenu en ligne |
| Actualisation complète | Lent | Correction urgente |
| Actualisation de répertoire | Moyen | Mise à jour par lot |
| Cache-Control: no-cache | Automatique | Scénarios API |
Stale-While-Revalidate en pratique
Cache-Control: max-age=60, stale-while-revalidate=86400
Seconde 0 : Requête → Cache MISS → Récupération d'origine → Retourne valeur fraîche
Seconde 30 : Requête → Cache HIT → Retourne valeur en cache
Seconde 90 : Requête → Cache STALE → Retourne ancienne valeur immédiatement + récupération d'origine en arrière-plan
Seconde 91 : Récupération d'origine terminée → Cache mis à jour
Seconde 120 : Requête → Cache HIT → Retourne nouvelle valeur
Effet : L'utilisateur n'attend jamais la récupération d'origine, mise à jour silencieuse en arrière-plan.
VI. Stratégie d'invalidation du cache
Hachage de contenu (recommandé)
<!-- Hachage généré à la compilation -->
<script src="/app.abc123.js"></script>
<link href="/style.def456.css">
// Configuration webpack
output: {
filename: '[name].[contenthash:8].js',
}
Paramètre de requête de version
<!-- Simple mais le CDN peut ne pas cacher les paramètres ?v= -->
<script src="/app.js?v=2.0.0"></script>
Problème : Certains CDN ignorent les paramètres de requête, empêchant la mise à jour du cache.
Horodatage (non recommandé)
<!-- Change à chaque fois, détruit complètement le cache -->
<script src="/app.js?t=1717315200"></script>
VII. Cache Service Worker
Stratégie Cache First
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request)
.then(cached => cached || fetch(event.request))
);
});
Stratégie Stale While Revalidate
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.open('dynamic').then(cache =>
cache.match(event.request).then(cached => {
const fetchPromise = fetch(event.request)
.then(response => {
cache.put(event.request, response.clone());
return response;
});
return cached || fetchPromise;
})
)
);
});
Sélection de la stratégie de cache
| Stratégie | Source prioritaire | Applicable |
|---|---|---|
| Cache First | Cache | Polices, images |
| Network First | Réseau | API, HTML |
| Stale While Revalidate | Cache + mise à jour en arrière-plan | CSS, JS |
| Network Only | Réseau | Requêtes non-GET |
| Cache Only | Cache | Pages hors ligne |
VIII. Débogage du cache
Chrome DevTools
- DevTools → Panneau Network
- Vérifier la colonne
Size:from disk cache: Succès du cache disquefrom memory cache: Succès du cache mémoire- Taille réelle : Échec
- Vérifier l'en-tête de réponse
Age: Temps passé en cache - Cocher Disable cache : Désactiver le cache (quand DevTools est ouvert)
Vérification avec curl
# Voir les en-têtes de réponse
curl -I https://example.com/style.css
# Envoyer une requête conditionnelle
curl -I -H 'If-None-Match: "abc123"' https://example.com/style.css
# 304 indique un succès du cache négocié
# Simuler un autre Accept-Encoding
curl -I -H 'Accept-Encoding: br' https://example.com/style.css
IX. Liste des meilleures pratiques
- HTML : no-cache, garantir que l'entrée est toujours à jour
- JS/CSS avec hachage : max-age=1 an + immutable
- API : must-revalidate, doit valider après expiration
- CDN : s-maxage pour contrôler la fréquence de récupération d'origine
- Vary : Accept-Encoding, distinguer les méthodes de compression
- Utiliser ETag plutôt que Last-Modified
- stale-while-revalidate pour une mise à jour imperceptible
- Service Worker pour compléter les capacités hors ligne