# Cache-Control (/fr/docs/web-security/security-headers/cache-control)



`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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching).

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 :

```http
Cache-Control: no-store
```

## Valeurs et ce que fait chacune [#valeurs-et-ce-que-fait-chacune]

| Directive            | Statut   | Description                                                                                                                                                      |
| -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `no-store`           | ✅ Bon    | La 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`            | ✅ Bon    | Les 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>`  | ✅ Bon    | Duré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>` | ✅ Bon    | Duré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`    | ✅ Bon    | Une 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`](/fr/docs/web-security/security-headers/cookie-security)
n'empêche pas la mise en cache : la [RFC 9111](https://httpwg.org/specs/rfc9111.html)
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 [#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](https://www.securityweek.com/details-34000-steam-users-exposed-during-ddos-attack/)).
  `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](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/04-Authentication_Testing/06-Testing_for_Browser_Cache_Weaknesses)).
* **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](https://portswigger.net/web-security/web-cache-deception).
* **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](https://portswigger.net/web-security/web-cache-poisoning)).
  Des directives de cache strictes réduisent ce qu'un cache empoisonné peut
  détenir.

## Risques sans le header [#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é](/tools/security-headers) 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 [#risques-et-pièges-à-lusage]

* **`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](https://developer.chrome.com/docs/web-platform/bfcache-ccns),
  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](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html)
  recommande d'envoyer
  [`Clear-Site-Data`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/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](https://developers.cloudflare.com/cache/concepts/cache-control/))
  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`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/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 [#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 :

   ```http
   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 :

   ```http
   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 :

   ```http
   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é :

   ```http
   Clear-Site-Data: "cache","cookies","storage"
   ```

6. Vérifiez les headers déployés avec le
   [scanner de headers de sécurité](/tools/security-headers), et
   contrôlez qu'aucune règle de cache du CDN ne remplace ce qu'envoie l'origine.

## Recommandation [#recommandation]

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

```http
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](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html)
et de [WSTG-ATHN-06](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/04-Authentication_Testing/06-Testing_for_Browser_Cache_Weaknesses) ;
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 [#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 [#faq]

### Quelle est la différence entre no-cache et no-store ? [#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](/fr/blog/no-cache-vs-no-store) pour la comparaison
complète.

### Faut-il mettre les pages de connexion en cache ? [#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 ? [#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 [#voir-aussi]

* [Vue d'ensemble des headers de sécurité](/fr/docs/web-security/security-headers)
* [Sécurité des cookies](/fr/docs/web-security/security-headers/cookie-security)
  pour les cookies de session dont ce header garde les réponses hors des caches
* [Scanner de headers de sécurité](/tools/security-headers) pour vérifier
  vos directives de cache et le reste de vos headers déployés

## Sources [#sources]

* [RFC 9111, cache HTTP](https://httpwg.org/specs/rfc9111.html)
* [MDN, Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control)
* [MDN, guide du cache HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Caching)
* [OWASP, cheat sheet de gestion des sessions](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html)
* [OWASP WSTG, tester les faiblesses du cache navigateur](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/04-Authentication_Testing/06-Testing_for_Browser_Cache_Weaknesses)
* [Chrome, back/forward cache avec Cache-Control no-store](https://developer.chrome.com/docs/web-platform/bfcache-ccns)
* [SecurityWeek, détails des 34 000 utilisateurs Steam exposés lors d'une attaque DDoS](https://www.securityweek.com/details-34000-steam-users-exposed-during-ddos-attack/)
