Cache-Control API - Maîtrisez le cache pour des API rapides et sûres

Xavier Moreau .

27 février 2026

Schéma APISIX Node A : 3 workers avec caches LRU (Layer 1), un cache partagé (Layer 2) et un cache L3. Le cache control header gère les requêtes.

L’en-tête HTTP Cache-Control, celui qu’on désigne parfois par cache control header, décide combien de temps une réponse peut rester en mémoire, qui peut la réutiliser et si elle doit être revérifiée avant d’être servie. Sur une API, ce réglage influence à la fois les performances, la charge serveur et la sécurité des données exposées. Ici, je vais aller droit au but: comprendre les directives utiles, choisir la bonne politique selon le type de réponse et éviter les erreurs qui ruinent un cache ou, pire, exposent des informations.

Les bons réglages de cache reposent surtout sur la fraîcheur, la portée et le niveau de risque

  • Cache-Control se lit toujours avec le contexte: navigateur, CDN, proxy, API.
  • no-cache permet de stocker puis de revalider; no-store interdit le stockage.
  • private protège les réponses personnalisées; public ouvre la porte aux caches partagés.
  • max-age, s-maxage, stale-while-revalidate et immutable servent surtout à réduire les allers-retours inutiles.
  • Vary est essentiel dès qu’une réponse change selon les cookies ou l’identité.

Ce que contrôle réellement cet en-tête

Je l’utilise comme un contrat entre le serveur et les caches: combien de temps une réponse peut être gardée, si elle peut être partagée entre utilisateurs et si elle doit être revérifiée avant d’être réutilisée. Sur une API, cela influence directement les temps de réponse, la bande passante et le nombre de hits sur l’origine.

La distinction la plus importante est simple: un cache privé vit dans le navigateur, alors qu’un cache partagé peut être utilisé par un CDN ou un proxy entre votre serveur et plusieurs clients. C’est pour ça qu’une réponse de profil utilisateur, même si elle est rapide à servir, ne doit jamais être traitée comme un catalogue public.

Je préfère aussi être explicite plutôt que de laisser le serveur ou le proxy deviner. Sans politique claire, vous obtenez parfois un comportement heuristique: acceptable pour un site statique, dangereux pour une API qui sert des données sensibles ou qui change souvent. La logique à garder en tête est la suivante: stocker, revalider ou interdire. C’est exactement ce que l’on va détailler maintenant avec les directives utiles.

Un ordinateur demande

Les directives qui comptent vraiment côté API

Je ne traite pas toutes les directives comme équivalentes. Certaines bloquent la conservation, d’autres optimisent la réutilisation et d’autres encore ajoutent une tolérance utile quand un backend a des pics ou des incidents.

Directive Effet concret Quand je l’utilise Piège fréquent
no-store La réponse ne doit pas être stockée par aucun cache. Données très sensibles, jetons, informations de session, réponses qui ne doivent laisser aucune trace locale. Beaucoup l’emploient par réflexe, alors qu’il est plus fort que nécessaire.
no-cache La réponse peut être stockée, mais elle doit être validée avant réutilisation. Contenu qui doit rester à jour sans interdire le cache, avec ETag ou Last-Modified. Ce n’est pas “ne pas cacher”. C’est “ne pas réutiliser sans vérification”.
max-age=... Durée pendant laquelle la réponse reste fraîche. Ressources publiques, listes, pages stables, données dont la cadence de mise à jour est connue. Un TTL trop long sur une donnée volatile crée des réponses obsolètes.
private La réponse ne doit être stockée que par un cache privé, côté navigateur. Réponses personnalisées, vues après connexion, contenu dépendant d’un cookie. Sans cette directive, une réponse individualisée peut fuiter vers un cache partagé.
public La réponse peut être conservée par un cache partagé. Contenu identique pour tout le monde, ou réponse que vous voulez rendre cacheable au bord du réseau. À éviter sur une réponse qui dépend de l’identité de l’utilisateur.
s-maxage=... TTL spécifique aux caches partagés, qui prime sur max-age pour eux. Quand vous voulez une durée différente entre navigateur et CDN. Ignoré par les caches privés, donc inutile si vous n’avez pas de cache partagé.
immutable Le cache peut considérer la réponse comme inchangée tant qu’elle est fraîche. Fichiers fingerprintés ou versionnés, comme des bundles JS ou CSS. Si l’URL ne change pas quand le contenu change, vous allez garder une mauvaise version.
stale-while-revalidate=... Le cache peut servir une version un peu ancienne pendant qu’il recharge en arrière-plan. Flux, listes, contenus qui tolèrent quelques secondes de retard. Très utile pour la fluidité, moins pour des données ultra sensibles au délai.
stale-if-error=... Une réponse ancienne peut être réutilisée en cas d’erreur amont. Services où une dégradation légère vaut mieux qu’une panne visible. Ne doit pas masquer durablement un problème réel du backend.
must-revalidate Si la réponse est périmée, elle doit être revérifiée avant réutilisation. Quand la fraîcheur est stricte et qu’un contenu obsolète est inacceptable. Souvent redondant si no-cache suffit déjà à exprimer l’intention.

Deux remarques m’évitent beaucoup d’erreurs: no-cache ne signifie pas “ne pas stocker”, et les directives de requête comme only-if-cached ou max-stale sont des demandes adressées au cache, pas une garantie que tous les intermédiaires obéiront. Sur une API, je construis donc d’abord la politique de réponse, puis j’ajoute éventuellement la validation conditionnelle avec ETag ou Last-Modified.

Une fois ces briques posées, le vrai travail consiste à choisir la politique adaptée à chaque type de réponse.

Choisir la bonne stratégie selon le type de réponse

Je pars toujours de la nature de la donnée, pas du désir abstrait d’“aller plus vite”. Une réponse publique et stable ne mérite pas le même traitement qu’un profil utilisateur, et une page HTML d’entrée n’a pas les mêmes contraintes qu’un fichier statique fingerprinté.

Type de réponse Réglage que je conseille Pourquoi Quand je changerais d’avis
Profil utilisateur, panier, données après connexion private, no-cache ou private, no-store si la sensibilité est maximale La réponse reste hors des caches partagés et doit être revérifiée avant usage. Je ne passe à no-store que si aucune conservation locale n’est acceptable.
Catalogue public, liste d’articles, données qui changent peu public, max-age=60, stale-while-revalidate=30 avec ETag Le client reçoit vite une réponse, puis le cache se rafraîchit sans bloquer l’utilisateur. Je baisse le TTL si la donnée évolue plus vite que prévu.
HTML de point d’entrée, route d’une SPA, page à revalider à chaque déploiement no-cache avec validation conditionnelle Le navigateur peut garder une copie, mais il vérifie systématiquement qu’elle est encore valable. Je ne mets no-store que si la page contient des données réellement sensibles.
Fichier versionné ou fingerprinté public, max-age=31536000, immutable Le contenu ne change pas sous la même URL, donc la revalidation devient inutile tant que la version est fraîche. Si le fichier change sans changement d’URL, la politique devient dangereuse.
Réponse servie via CDN avec durée différente entre bordure et navigateur public, max-age=60, s-maxage=300 Le navigateur reste assez réactif, tandis que le cache partagé garde la réponse plus longtemps. Je l’utilise seulement quand je maîtrise vraiment la couche de cache intermédiaire.

Dès qu’une réponse dépend d’un cookie, d’un jeton ou d’un autre signal d’identité, j’ajoute aussi la logique de variation qui va avec, le plus souvent via Vary: Cookie. Sans ça, vous pouvez avoir la bonne politique de durée mais la mauvaise séparation entre variantes.

Quand la règle est claire, la configuration devient presque mécanique; les exemples ci-dessous montrent comment je l’écris concrètement.

Des exemples concrets qui tiennent debout en production

Une API publique qui peut tolérer une minute de retard

Je choisis cette approche pour des contenus comme des listes d’articles, des tendances ou des données qui peuvent être un peu périmées sans casser l’expérience. L’idée n’est pas de tricher sur la fraîcheur, mais d’éviter que chaque visite provoque un aller-retour inutile vers l’origine.

Cache-Control: public, max-age=60, stale-while-revalidate=30
ETag: "articles-v42"

Le couple max-age + stale-while-revalidate donne une bonne sensation de rapidité, et ETag réduit encore le coût des revalidations. Si votre contenu change plus vite, je raccourcis simplement la fenêtre.

Une réponse personnalisée après authentification

Dès qu’un endpoint sert un profil, un panier ou une configuration propre à l’utilisateur, je verrouille la portée avant de discuter de performance. C’est là qu’une configuration propre évite les fuites de données entre caches partagés et sessions différentes.

Cache-Control: private, no-cache
Vary: Cookie
ETag: "profile-17"

private limite la conservation au navigateur, no-cache force la validation avant réutilisation, et Vary: Cookie protège contre le mélange de variantes. Si les données sont vraiment sensibles, je remplace no-cache par no-store, mais seulement quand la conservation locale n’a aucun intérêt.

Lire aussi : TypeScript & Node.js - Backend robuste et API évolutive

Des assets front versionnés

Pour un bundle JS, un fichier CSS ou une image fingerprintée, je préfère une stratégie agressive et simple. Tant que l’URL change à chaque nouvelle version, le cache peut conserver le fichier longtemps sans risque de servir une vieille ressource.

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

Ici, la vraie condition de réussite n’est pas le header lui-même, c’est le fait que le nom du fichier change quand le contenu change. Sans versionnement, immutable devient une promesse trop forte.

Même une bonne configuration peut poser problème si elle est contradictoire ou mal testée. C’est là que les erreurs classiques apparaissent.

Les erreurs qui cassent le cache ou brouillent la sécurité

Je vois revenir les mêmes fautes, souvent parce qu’on cherche une “bonne pratique” générique au lieu de raisonner par type de donnée. Le cache n’est pas un interrupteur magique; c’est un compromis entre vitesse, fraîcheur et contrôle.
  • Confondre no-cache et no-store. Le premier autorise le stockage avec validation, le second interdit le stockage.
  • Mettre public par réflexe sur une réponse qui dépend d’un cookie ou d’une session.
  • Oublier Vary quand la réponse change selon l’identité, la langue ou d’autres paramètres de requête.
  • Donner un TTL très long à une page HTML qui change souvent, sans versionnement clair.
  • Empiler plusieurs directives contradictoires dans l’espoir de “couvrir tous les cas”. En pratique, cela rend la politique illisible.
  • Réserver no-store à des cas qui ne le nécessitent pas. Sur une page complète, cela peut aussi dégrader l’expérience de retour arrière.
  • Tester uniquement le cache du navigateur et oublier le comportement d’un CDN ou d’un proxy partagé.

Je préfère une règle simple: chaque directive doit répondre à un besoin précis. Si vous ne pouvez pas expliquer pourquoi elle est là, elle est probablement de trop. Et si la réponse est vraiment critique, je vérifie aussi que la logique de validation et de variation est cohérente, pas seulement le TTL.

Pour éviter de découvrir ces défauts en production, je termine toujours par une vérification simple mais méthodique.

Ce que je vérifierais avant de figer la politique de cache

Le plus efficace reste de tester trois scénarios: une requête froide, une requête répétée et une requête après expiration. J’observe alors si la réponse est servie depuis le cache, si elle est validée correctement et si les bons indicateurs apparaissent.

  1. Inspecter les en-têtes avec les outils de développement ou un simple curl -I.
  2. Rejouer la requête pour vérifier qu’un 304 Not Modified apparaît quand c’est attendu.
  3. Contrôler la durée réelle en comparant le comportement avant et après expiration du TTL.
  4. Vérifier le cache partagé séparément du cache navigateur si vous passez par un CDN.
  5. Surveiller Age et Cache-Status quand ils sont disponibles, pour voir ce que le cache a réellement fait.

Au fond, une bonne politique de cache n’est pas la plus agressive possible; c’est celle qui sert vite sans jamais réutiliser la mauvaise réponse. Sur un backend ou une API, je pars presque toujours de trois questions: qui a le droit de garder cette donnée, pendant combien de temps, et à quelles conditions doit-elle être revérifiée? Si vous répondez proprement à ces trois points, le reste devient simple.

Questions fréquentes

L'en-tête HTTP Cache-Control définit la politique de mise en cache d'une ressource. Il indique aux navigateurs, CDN et proxies combien de temps une réponse peut être stockée, qui peut la réutiliser et si elle doit être revalidée avant d'être servie. Essentiel pour la performance et la sécurité des API.
no-cache permet de stocker la réponse, mais exige une revalidation auprès du serveur avant chaque réutilisation. no-store, en revanche, interdit tout stockage de la réponse par n'importe quel cache, garantissant que les données sensibles ne laissent aucune trace.
La directive private doit être utilisée pour les réponses contenant des données personnalisées ou sensibles (ex: profil utilisateur, panier). Elle assure que la réponse ne sera mise en cache que par le navigateur de l'utilisateur final, et non par des caches partagés comme les CDN, évitant ainsi les fuites d'informations.
Pour les fichiers statiques versionnés (ex: `app.12345.js`), utilisez `Cache-Control: public, max-age=31536000, immutable`. Cela indique aux caches de conserver la ressource pendant un an et de ne pas la revalider, car son contenu est immuable tant que l'URL ne change pas.
Vary: Cookie est crucial lorsque la réponse d'une API dépend du cookie de session ou d'authentification. Il signale aux caches que la réponse varie en fonction de l'en-tête `Cookie`, empêchant ainsi un cache de servir une réponse personnalisée à un utilisateur différent, protégeant contre les fuites de données.

Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

cache control header cache-control header api cache-control directives api
Autor Xavier Moreau
Xavier Moreau
Je m'appelle Xavier Moreau et je cumule 14 ans d'expérience dans le développement web, avec un accent particulier sur JavaScript, le backend, le NoSQL et la sécurité. Mon intérêt pour ces domaines a émergé dès mes débuts dans la programmation, où j'ai découvert la puissance des technologies web et leur capacité à transformer des idées en réalité. J'aime expliquer des concepts complexes de manière accessible, en aidant les lecteurs à naviguer dans les défis techniques qu'ils rencontrent. Au fil des ans, j'ai développé une expertise solide en vérifiant mes sources, en comparant les informations et en simplifiant des sujets parfois ardus. Je m'efforce toujours de fournir des contenus utiles, précis et à jour, en suivant les tendances du secteur et en organisant mes connaissances de manière claire. Mon objectif est d'accompagner les passionnés et les professionnels du développement web dans leur quête de compréhension et d'innovation.

Commentaires (0)

Ajouter un commentaire