Qui écoute cet en-tête
Une même réponse traverse plusieurs caches : le navigateur du visiteur (privé — il sert une personne), la périphérie du CDN (partagé — il sert tout le monde), et parfois un proxy intermédiaire. Cache-Control est la façon dont l'origine s'adresse à tous en même temps, d'où l'existence de directives qui les visent séparément.
Cette distinction commande tout le reste. Un tableau de bord connecté peut être mis en cache dans le navigateur de cet utilisateur et ne doit jamais l'être en périphérie, où un autre utilisateur pourrait le recevoir. Une page marketing publique, c'est l'inverse : cache long en périphérie, court dans le navigateur. Décider d'abord « privé ou partagé » rend tous les autres choix évidents.
Les directives qui comptent vraiment
max-age=N — réutilisable pendant N secondes sans redemander. Le réglage principal.
s-maxage=N — pareil, mais uniquement pour les caches partagés (le CDN). Quand elle est présente, la périphérie lui obéit et ignore max-age : vous pouvez ainsi garder un contenu une heure en périphérie pendant que les navigateurs ne le gardent qu'une minute.
public / private — public autorise le stockage par n'importe quel cache ; private limite le stockage au navigateur de la personne. Tout ce qui est propre à un utilisateur doit être private.
no-cache — stockez-le, mais revalidez auprès de l'origine avant chaque réutilisation. Peu coûteux quand rien n'a changé : le serveur peut répondre 304 Not Modified sans corps.
no-store — ne l'écrivez dans aucun cache, ni en mémoire ni sur disque. Réservez cette directive aux réponses réellement sensibles ; ce n'est pas « merci d'être frais », c'est « n'en gardez aucune copie ».
immutable — ce contenu ne changera jamais à cette URL, donc inutile même de revalider au rechargement. Vrai uniquement pour les noms de fichiers versionnés.
stale-while-revalidate=N — après expiration, continuez à servir la copie périmée pendant N secondes au plus pendant qu'une version fraîche est récupérée en arrière-plan. Le visiteur n'attend jamais la nouvelle requête.
Trois recettes qui couvrent la plupart des sites
Assets statiques versionnés — le nom du fichier change dès que le contenu change, l'URL peut donc être mise en cache indéfiniment sans risque. C'est le gain de cache le plus important et le plus sûr qui soit.
Pages HTML — l'URL reste la même alors que le contenu change : il leur faut donc une durée de vie courte. Un max-age bref associé à stale-while-revalidate vous donne la vitesse sans servir la page d'hier.
Réponses privées ou personnalisées — tableaux de bord, paniers, tout ce qui se trouve derrière une authentification. Tenez-les hors des caches partagés ; le navigateur peut les conserver brièvement si cela vous convient.
Points de départ à copier-coller — ajustez les chiffres à votre rythme de publication
# versioned assets: /js/app.a1b2c3.js
Cache-Control: public, max-age=31536000, immutable
# HTML pages (short at the browser, longer at the edge, no waiting on refresh)
Cache-Control: public, max-age=60, s-maxage=600, stale-while-revalidate=86400
# per-user pages: never at the edge
Cache-Control: private, no-store
# an API response that changes often but can lag a little
Cache-Control: public, max-age=0, s-maxage=30, stale-while-revalidate=60
no-cache ou no-store : l'erreur à éviter
Ces deux directives se lisent comme des synonymes et ne se comportent pas du tout pareil. no-cache autorise le stockage mais impose une revalidation avant réutilisation — la copie reste, et lorsqu'elle est encore à jour le serveur répond 304 sans corps, ce qui est très économique. no-store interdit de conserver la réponse où que ce soit.
Choisir no-store quand vous vouliez dire no-cache jette toutes les optimisations sans aucun bénéfice : chaque requête devient un téléchargement complet même quand rien n'a changé. N'utilisez no-store que lorsque la copie stockée est elle-même le problème — relevés bancaires, pages de réinitialisation de mot de passe, tout ce qui ne doit pas traîner dans le cache d'une machine partagée. Pour « toujours afficher la dernière version », no-cache est la réponse correcte et bien moins coûteuse.
Comment cela interagit avec votre CDN
Les en-têtes envoyés par votre origine sont ce à quoi la périphérie obéit pour décider combien de temps garder une copie — ce qui fait de Cache-Control la surface de pilotage de toute votre chaîne de diffusion, et non un détail de navigateur. Envoyez s-maxage et la périphérie s'y conforme ; envoyez no-store et la périphérie refuse de mettre quoi que ce soit en cache, si bien que chaque requête remonte à votre origine et que vous avez de fait désactivé le CDN pour cette réponse.
Sur cdn.com.tr, vous pouvez aussi définir le comportement de cache par règle de diffusion dans le panneau lorsque vous ne pouvez pas modifier les en-têtes de l'application — pratique pour des applications historiques auxquelles vous préférez ne pas toucher. Et quand vous changez ce que renvoie une URL déjà en cache, rappelez-vous que la périphérie détient encore la copie précédente jusqu'à son expiration : c'est justement à cela que sert une purge.
Vérifier ce que vous envoyez réellement
Les suppositions sur les en-têtes sont souvent fausses — un framework, un plugin ou une valeur par défaut du serveur web écrase fréquemment ce que vous croyez avoir configuré. Vérifiez avec une commande par type d'URL, et contrôlez à la fois un asset versionné et une page HTML, puisqu'ils devraient être complètement différents. Guettez aussi un Set-Cookie sur une réponse que vous vouliez mettre en cache publiquement : beaucoup de caches refusent de stocker ces réponses, et c'est une raison courante pour laquelle une page n'est mystérieusement jamais mise en cache.
Lire les vrais en-têtes de réponse
# see what the edge and origin actually say
curl -sI https://example.com/ | grep -i "cache-control\|age\|set-cookie"
# compare a versioned asset (should be a long max-age)
curl -sI https://example.com/js/app.a1b2c3.js | grep -i cache-control
Questions fréquentes
Quel max-age choisir pour des pages HTML ?
Court — de quelques secondes à quelques minutes — car l'URL reste la même alors que le contenu change. Associez-le à un s-maxage plus long en périphérie et à stale-while-revalidate pour que les visiteurs obtiennent une réponse instantanée pendant que le rafraîchissement se fait en arrière-plan.
Faut-il encore utiliser Expires en plus de Cache-Control ?
Non. Cache-Control remplace Expires et l'emporte partout où les deux apparaissent. Expires ne compte que pour des clients extrêmement anciens ; l'envoyer aussi est sans danger mais n'apporte rien.
Pourquoi ma page n'est-elle pas mise en cache malgré un long max-age ?
Le plus souvent à cause d'un en-tête Set-Cookie sur la réponse, d'une directive private ou no-store quelque part dans la chaîne, ou d'une chaîne de requête qui rend chaque requête unique comme clé de cache. Inspectez les en-têtes réels de la réponse avant de changer la configuration.
immutable veut-il vraiment dire « pour toujours » ?
Cela veut dire « le contenu à cette URL ne changera pas », donc les caches sautent complètement la revalidation. Ce n'est vrai que pour des noms de fichiers versionnés. Mettre immutable sur une URL que vous écrasez ensuite, c'est ainsi que des visiteurs restent bloqués sur un ancien fichier sans moyen propre d'y remédier.