HTTP en profondeur - 08 - Le cache HTTP : éviter les requêtes inutiles

Cache-Control, ETag, Last-Modified, 304 Not Modified et Vary : tout ce qui permet au navigateur de ne pas redemander ce qu'il a deja.

08 - Le cache HTTP : éviter les requêtes inutiles

Ce que tu vas apprendre

  • Les directives Cache-Control et leur effet réel
  • La validation conditionnelle avec ETag et Last-Modified
  • Le rôle du header Vary pour les caches partages
  • Comment un CDN utilise ces mecanismes

Prerequisites


Le cache HTTP est probablement le mecanisme le plus mal compris du protocole. Tout le monde sait que "ca met en cache", mais quand tu demandes la différence entre no-cache et no-store, le silence est assourdissant.

Je me souviens d'un bug en production ou une page affichait des donnees d'un autre utilisateur. Le coupable : un Cache-Control: public sur une réponse personnalisee. Le CDN avait mis en cache la page du premier visiteur et la servait a tout le monde. Deux caractères de trop, trois heures de debug.

Cache-Control : le chef d'orchestre

Le header Cache-Control dans la réponse dicte le comportement du cache. Voici les directives une par une.

max-age

Cache-Control: max-age=3600

Le navigateur garde la réponse pendant 3600 secondes. Pendant cette duree, il ne contacte meme pas le serveur. Zero requête réseau. C'est le cache dit "frais".

no-cache

Le nom est trompeur. no-cache ne signifie pas "ne mets pas en cache". Ca signifie "mets en cache, mais revalide a chaque fois auprès du serveur avant d'utiliser la copie". Le navigateur stocke la réponse, mais il demande au serveur si elle est encore bonne avant de la servir.

no-store

Ca, c'est le vrai "ne mets rien en cache". Ni mémoire, ni disque, rien. Pour les donnees sensibles (comptes bancaires, tokens), c'est no-store qu'il te faut.

public vs private

public autorise les caches partages (CDN, proxy d'entreprise) a stocker la réponse. private restreint au cache du navigateur uniquement. Une page avec le nom de l'utilisateur doit etre private. Un fichier CSS peut etre public.

s-maxage

Comme max-age, mais uniquement pour les caches partages. Tu peux avoir max-age=60, s-maxage=3600 : le navigateur garde 1 minute, le CDN garde 1 heure.

must-revalidate

Une fois la réponse perimee, le cache doit revalider auprès du serveur. Sans cette directive, certains caches servent du contenu perime en cas de problème réseau. Avec must-revalidate, c'est interdit : soit tu revalides, soit tu renvoies une erreur.

stale-while-revalidate

Cache-Control: max-age=3600, stale-while-revalidate=60

Pendant les 60 secondes apres expiration, le cache sert la version perimee tout en demandant la nouvelle en arriere-plan. L'utilisateur voit la page instantanement, et la prochaine visite aura la version à jour. C'est un compromis elegant entre fraicheur et performance.

Validation conditionnelle : le 304 Not Modified

Le cache expire. Mais ca ne veut pas dire que le contenu a change. La validation conditionnelle évité de retelecharger un fichier identique.

ETag + If-None-Match

Le serveur envoie un identifiant unique avec la réponse :

HTTP/1.1 200 OK
ETag: "abc123def456"
Content-Length: 45000

Au prochain acces, le navigateur demande :

GET /style.css HTTP/1.1
If-None-Match: "abc123def456"

Si le contenu n'a pas change, le serveur répond :

HTTP/1.1 304 Not Modified

Pas de body. Juste les headers. Le navigateur utilise sa copie locale. Tu economises la bande passante du transfert complet.

Last-Modified + If-Modified-Since

Meme principe, mais base sur la date :

HTTP/1.1 200 OK
Last-Modified: Mon, 15 Mar 2026 10:30:00 GMT

Le navigateur envoie ensuite :

GET /style.css HTTP/1.1
If-Modified-Since: Mon, 15 Mar 2026 10:30:00 GMT

ETag est plus precis (un fichier peut etre modifie puis revenir a l'identique), mais Last-Modified est plus simple a implementer. La plupart des serveurs web le font automatiquement pour les fichiers statiques.

Le header Vary : quand la meme URL donne des réponses différentes

Imagine une API qui renvoie du JSON ou du XML selon le header Accept. Le CDN cache la première réponse. Le deuxieme client demande un format différent et recoit la version cachee dans le mauvais format.

Le header Vary resout ca :

Vary: Accept, Accept-Encoding

Le cache créé une entree séparée pour chaque combinaison de valeurs des headers listes dans Vary. Meme URL, mais des caches différents selon Accept et Accept-Encoding.

Attention : Vary: * désactivé complètement le cache. C'est rarement ce que tu veux.

CDN et cache : la couche supplementaire

Un CDN (Cloudflare, Fastly, CloudFront) est un cache partage distribue geographiquement. Il respecte les memes headers Cache-Control, mais avec des subtilites.

Le pattern classique pour les assets statiques :

Cache-Control: public, max-age=31536000, immutable

Un an de cache. Le immutable dit au navigateur de ne meme pas revalider quand l'utilisateur recharge la page. Ca fonctionne parce que les fichiers ont un hash dans leur nom (style.a3f2b1.css). Si le contenu change, l'URL change.

Pour les pages HTML dynamiques, c'est différent :

Cache-Control: no-cache

Le CDN et le navigateur gardent la page, mais revalident systématiquement. Le serveur renvoie un 304 si rien n'a change, ou un 200 avec le nouveau contenu.

Sur paltemps.fr, on utilise ce pattern exact : assets immutables avec hash, HTML en no-cache. Le Time to First Byte tombe a quelques millisecondes pour les visiteurs réguliers.

Les erreurs classiques

Oublier private sur du contenu personnalise. Le CDN cache la réponse et la sert a tout le monde. Bug critique.

Mettre no-cache en pensant "pas de cache". Les développeurs confondent no-cache et no-store en permanence. Si tu veux zero cache, c'est no-store.

Ignorer Vary. Ton API sert du gzip et du brotli selon Accept-Encoding. Sans Vary: Accept-Encoding, le CDN peut servir du brotli a un vieux navigateur qui ne le supporte pas.

Des max-age trop longs sur du HTML. Changer une page HTML cachee un an chez le client, c'est impossible. Tu ne peux pas invalider le cache du navigateur. Les assets ont des hashes, pas le HTML.


Résumé

  • max-age contrôle la duree de cache, no-cache force la revalidation, no-store interdit tout stockage
  • public autorise les caches partages, private restreint au navigateur
  • ETag et Last-Modified permettent la validation conditionnelle avec des réponses 304
  • Vary séparé les entrees de cache selon les headers de la requête
  • Les CDN respectent ces memes mecanismes avec s-maxage pour un contrôle spécifique
  • Le pattern hash + immutable pour les assets, no-cache pour le HTML

Article précédent : 07 - Cookies

Article suivant : 09 - TLS et HTTPS

Sources

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.