CentralCSP
Headers de sécurité

Cache-Control

Le volet sécurité de Cache-Control, garder les réponses avec données personnelles ou de session hors des caches partagés et du cache navigateur avec no-store.

Dernière mise à jour:

Cache-Control est le header de réponse qui décide si une copie de la réponse peut être stockée et réutilisée, par le cache privé du navigateur et par les caches partagés (CDN, reverse proxies, proxies d'entreprise) qui servent une même réponse stockée à de nombreux utilisateurs. Les caches stockent et réutilisent par conception : HTTP est fait pour mettre en cache autant que possible, donc même une réponse sans aucun header de cache peut être stockée et réutilisée. Cela s'appelle le cache heuristique.

Ce défaut est le problème de sécurité. Une page personnalisée (un tableau de bord de compte, une confirmation de commande) stockée dans un cache partagé peut être servie à un autre utilisateur. Cette page ne couvre que ce volet sécurité, garder les réponses sensibles hors des caches ; l'optimisation des performances avec ce header sort du cadre.

Pour une réponse qui porte des données personnelles ou de session :

Cache-Control: no-store

Valeurs et ce que fait chacune

DirectiveStatutDescription
no-store✅ BonLa directive de sécurité. Aucun cache, privé ou partagé, ne peut stocker la moindre partie de la requête ou de la réponse.
private✅ BonLes caches partagés ne doivent pas stocker la réponse. Le cache navigateur le peut encore, donc ce n'est pas un substitut à no-store.
no-cache❌ RisquéDangereux comme contrôle de confidentialité. Cela signifie revalider avant réutilisation, pas ne pas stocker ; les octets atterrissent quand même sur le disque.
public❌ RisquéLaisse n'importe quel cache stocker la réponse, y compris certains qui la refuseraient autrement. Jamais sur des réponses personnalisées.
max-age=<seconds>✅ BonDurée de fraîcheur, un contrôle de performance. max-age=0 marque la réponse comme périmée immédiatement.
s-maxage=<seconds>✅ BonDurée de fraîcheur pour les caches partagés uniquement (réglage CDN) ; remplace max-age pour eux. Effet de bord ci-dessous.
must-revalidate✅ BonUne réponse devenue périmée doit être revalidée avant réutilisation. Effet de bord ci-dessous.

Deux défauts prennent presque tout le monde au dépourvu. D'abord, Set-Cookie n'empêche pas la mise en cache : la RFC 9111 indique qu'une réponse cacheable avec un header Set-Cookie peut être, et est souvent, utilisée pour satisfaire des requêtes ultérieures. Ensuite, le cache heuristique fait qu'une réponse sans aucun header de cache peut tout de même être stockée, le cache inventant sa propre durée de fraîcheur. N'envoyer rien n'est pas une politique ; c'est une permission.

Une nuance sur s-maxage et must-revalidate : les caches partagés ne doivent pas stocker une réponse à une requête qui portait un header Authorization, sauf si public, s-maxage ou must-revalidate l'autorise explicitement. Ces deux directives débloquent donc discrètement la mise en cache partagée des réponses à Authorization. Et cette protection intégrée ne couvre que le header Authorization, pas les sessions par cookie, qui sont pourtant le mode d'authentification de la plupart des applications modernes.

Contre quoi cela protège

  • D'autres utilisateurs qui lisent des pages personnalisées depuis un cache partagé. Ce n'est pas théorique. Le 25 décembre 2015, pendant une attaque par déni de service, le changement de configuration d'un partenaire de cache a mis en cache par erreur le trafic web de Steam pour les utilisateurs authentifiés, et environ 34 000 utilisateurs se sont vu servir les pages Store d'autres utilisateurs, exposant des adresses de facturation, des adresses e-mail, des historiques d'achat et les derniers chiffres de numéros de téléphone et de cartes de paiement (couverture de SecurityWeek). no-store (ou au minimum private) sur ces réponses est ce qui empêche un cache partagé de jamais les détenir.
  • La lecture après déconnexion sur des machines partagées. Sur un ordinateur de bibliothèque ou de bureau, la personne suivante peut appuyer sur le bouton retour et réafficher une page de compte mise en cache après la déconnexion de l'utilisateur précédent. OWASP teste exactement cela (WSTG-ATHN-06, tester les faiblesses du cache navigateur).
  • La web cache deception. Un attaquant piège un cache pour qu'il stocke une page dynamique sensible via des astuces de chemin comme /account/settings.css ; marquer les ressources dynamiques no-store et private est la défense documentée par PortSwigger.
  • Le web cache poisoning. Le pendant côté intégrité : un attaquant fait stocker une réponse malveillante servie ensuite à d'autres utilisateurs (PortSwigger sur le web cache poisoning). Des directives de cache strictes réduisent ce qu'un cache empoisonné peut détenir.

Risques sans le header

Sans le header, les réponses sensibles sont stockables par défaut. Le cache heuristique laisse les caches stocker et réutiliser des réponses sans aucun header de cache, et Set-Cookie n'empêche pas cela, donc « ça pose un cookie de session » n'est aucune protection. Une page personnalisée servie sans directive peut rester dans le cache navigateur d'une machine partagée et, pire, dans un cache partagé où elle peut répondre à la requête d'un autre utilisateur.

C'est exactement ce que le scanner de headers de sécurité signale par un constat « réponse sensible cacheable » : une réponse qui pose ou exige un cookie de session, sans directive (no-store ou private) empêchant le stockage en cache partagé. Le correctif est toujours d'envoyer la directive depuis le serveur ; aucun cache ne la déduira pour vous.

Risques et pièges à l'usage

  • no-cache n'est pas no-store. La confusion classique. no-cache signifie que le cache peut stocker la réponse mais doit la revalider avant réutilisation. Comme contrôle de confidentialité, il ne fait rien : les octets sont toujours sur le disque. Quand vous voulez dire ne pas stocker, envoyez no-store.
  • private laisse encore le navigateur stocker. Il n'exclut que les caches partagés. Sur une machine partagée, la page est toujours dans le cache disque du profil, donc les réponses sensibles ont besoin de no-store, pas de private.
  • Le back/forward cache ignore vos directives. no-cache et must-revalidate n'ont jamais garanti la revalidation lors d'une navigation dans l'historique, et depuis le déploiement de Chrome en 2025, même les pages no-store peuvent être restaurées depuis le back/forward cache (avec des mitigations : l'entrée est évincée quand les cookies changent, et elle vit au plus 3 minutes). La conséquence : la déconnexion doit réellement invalider la session, pas seulement quitter la page, et le cheat sheet OWASP sur la gestion des sessions recommande d'envoyer Clear-Site-Data à la déconnexion.
  • Le CDN peut vous remplacer. Les CDN respectent le Cache-Control de l'origine par défaut, mais la configuration à l'edge (par exemple les cache rules de Cloudflare) peut le remplacer, ce qui est arrivé lors de l'incident Steam. Auditez les règles de cache du CDN en même temps que le header.
  • Pragma et Expires sont hérités. La RFC 9111 déprécie Pragma (historiquement réservé aux requêtes et jamais spécifié pour les réponses), et Expires est ignoré dès que max-age est présent. Aucun des deux n'est nécessaire pour un navigateur moderne.

Comment le mettre en place

  1. Classez vos réponses en trois groupes : sensibles (tout ce qui porte des données de session ou personnelles), personnalisées mais que le navigateur peut mettre en cache sans risque, et ressources statiques à empreinte (fingerprinted).

  2. Envoyez no-store sur chaque réponse sensible, depuis le serveur, par réponse :

    Cache-Control: no-store
  3. Envoyez private, no-cache sur les réponses personnalisées que le navigateur peut garder mais que les caches partagés ne doivent pas toucher :

    Cache-Control: private, no-cache
  4. Gardez une longue fraîcheur sur les ressources statiques dont le nom de fichier porte un hash ou une version ; ce sont les mêmes octets pour chaque utilisateur :

    Cache-Control: max-age=31536000, immutable
  5. À la déconnexion, invalidez la session côté serveur et envoyez Clear-Site-Data pour que le navigateur abandonne ce qu'il a stocké :

    Clear-Site-Data: "cache","cookies","storage"
  6. Vérifiez les headers déployés avec le scanner de headers de sécurité, et contrôlez qu'aucune règle de cache du CDN ne remplace ce qu'envoie l'origine.

Recommandation

Envoyez Cache-Control: no-store sur chaque réponse qui porte des données de session ou personnelles :

Cache-Control: no-store

Envoyez no-store sur toute réponse contenant des identifiants de session ou des données personnelles, la recommandation du cheat sheet OWASP sur la gestion des sessions et de WSTG-ATHN-06 ; avec no-store en place, les directives supplémentaires et Expires sont généralement inutiles pour les navigateurs modernes. Appliquez-le par réponse, jamais sur tout le site : les pages sensibles reçoivent no-store, les pages personnalisées reçoivent private, no-cache, et les ressources statiques à empreinte gardent leur long max-age.

Prise en charge par les navigateurs

Baseline largement disponible : les directives de réponse de cette page fonctionnent dans tous les navigateurs, proxies et CDN actuels, donc la prise en charge est de fait universelle. Ce qu'il faut vérifier n'est pas le navigateur mais l'edge : un CDN respecte votre header par défaut mais peut être configuré pour le remplacer, donc le comportement déployé ne vaut que ce que valent les règles de cache devant l'origine.

FAQ

Quelle est la différence entre no-cache et no-store ?

no-store dit aux caches de ne garder aucune copie de la réponse. no-cache autorise le stockage d'une copie mais exige une revalidation auprès de l'origine avant toute réutilisation, donc les octets restent sur le disque. Quand vous voulez dire ne pas stocker une réponse sensible, utilisez no-store. Voir no-cache vs no-store pour la comparaison complète.

Faut-il mettre les pages de connexion en cache ?

Non. La connexion, le compte et toute réponse qui porte ou exige une session doivent envoyer Cache-Control: no-store, en suivant les recommandations OWASP sur la gestion des sessions. Sans cela, un cache partagé peut stocker la page personnalisée et la servir à un autre utilisateur, et le bouton retour peut la réafficher après la déconnexion.

Cache-Control private empêche-t-il le navigateur de mettre en cache ?

Non. private dit seulement aux caches partagés, comme les CDN et les proxies d'entreprise, de ne pas stocker la réponse ; le cache propre du navigateur peut encore la garder. Pour empêcher le stockage partout, y compris sur le disque dans le navigateur, utilisez plutôt no-store. private restreint le public, il n'arrête pas la mise en cache.

Voir aussi

Sources

On this page