Pourquoi votre site affiche d'anciens fichiers : diagnostiquer Cache-Control et les ETags avec cURL
Vous déployez une nouvelle feuille de style, actualisez votre site et voyez toujours l’ancien design. Un collègue obtient immédiatement la nouvelle version. Un client en voit encore une autre. Le navigateur conserve-t-il un fichier en cache ? Un CDN renvoie-t-il une ancienne réponse ? Ou le serveur fournit-il un contenu différent de celui attendu ?
Les problèmes de cache deviennent plus faciles à comprendre lorsque vous examinez la réponse HTTP au lieu d’actualiser la page sans arrêt. Deux en-têtes, Cache-Control et ETag, expliquent une grande partie du mécanisme. cURL permet de les examiner sans les comportements supplémentaires d’une page dans le navigateur.
Vous pouvez préparer une requête avec le générateur de commandes cURL de Web2Generators, puis exécuter et adapter la commande dans votre terminal. Le générateur crée les commandes localement ; il n’exécute pas les requêtes à votre place.
Deux questions distinctes sur le cache
La mise en cache HTTP repose sur deux décisions :
- Peut-on réutiliser une réponse stockée sans contacter le serveur ? Les directives de fraîcheur comme
max-ageaident à répondre à cette question. - Si la réponse stockée doit être vérifiée, la ressource a-t-elle changé ? Les validateurs comme
ETagouLast-Modifiedrépondent à celle-ci.
Cette distinction explique pourquoi un navigateur peut afficher une page en cache sans nouvelle réponse réseau, ou envoyer une requête sans télécharger à nouveau tout le fichier.
Le guide MDN sur la mise en cache HTTP décrit le mécanisme et ses compromis plus en détail.
Étape 1 : récupérer les en-têtes de réponse
Commencez par une page ou une ressource non sensible sur un site que vous êtes autorisé à tester. Pour afficher les en-têtes de réponse avec le corps, utilisez --include :
curl --include --url 'https://example.com/assets/site.css'
example.com sert ici d’illustration. Remplacez cette adresse par une URL réelle de votre site. Selon le serveur, une réponse réussie pourrait contenir :
HTTP/2 200
content-type: text/css
cache-control: public, max-age=3600
etag: "build-42"
Ces en-têtes fictifs décrivent une durée de fraîcheur d’une heure et un validateur nommé "build-42". Cette heure dépend de l’âge de la réponse ; elle ne recommence pas chaque fois qu’un navigateur la reçoit.
Le générateur Web2Generators prend en charge les méthodes de requête, les en-têtes personnalisés et la sortie détaillée. Pour un premier GET, saisissez l’URL, laissez la vérification TLS activée et copiez la commande. Vous pouvez ensuite ajouter --include à la main, ou utiliser l’option de sortie détaillée du générateur pour examiner la requête et la réponse. Le guide des commandes cURL présente d’autres options et les règles de citation du shell.
Étape 2 : interpréter les directives Cache-Control courantes
Ces directives sont liées, mais ne sont pas interchangeables :
| Directive | Signification pratique |
|---|---|
max-age=3600 | Une réponse peut être considérée comme fraîche jusqu’à un âge de 3 600 secondes selon les règles habituelles du cache HTTP. |
no-cache | Le stockage est autorisé, mais la réponse stockée doit être revalidée avant réutilisation. |
no-store | Demande au cache de ne pas stocker la réponse. |
private | Réserve la réponse à un cache privé, et non à un cache intermédiaire partagé. |
public | Autorise explicitement le stockage dans les caches partagés lorsque les autres conditions sont remplies. |
La directive souvent mal comprise est no-cache : elle ne signifie pas “ne jamais stocker”. Elle interdit de réutiliser une réponse stockée sans vérification auprès de l’origine ou d’un cache capable de la valider.
Examinez aussi Age, Expires et les éventuels en-têtes propres au CDN décrits dans sa documentation. Par exemple, Age: 3500 laisse environ 100 secondes sur une durée max-age=3600, avant de tenir compte du temps de transit. Un intermédiaire peut influencer la réponse qui parvient au navigateur. Un seul en-tête ne suffit pas à expliquer toute la situation.
Étape 3 : revalider avec un ETag
Un ETag est un identifiant fourni par le serveur pour une représentation d’une ressource. Après avoir récupéré un véritable ETag dans une réponse, renvoyez-le dans If-None-Match :
curl --include \
--url 'https://example.com/assets/site.css' \
--header 'If-None-Match: "build-42"'
Si la représentation sélectionnée correspond toujours et que les conditions de la requête sont remplies, le serveur peut répondre :
HTTP/2 304
etag: "build-42"
cache-control: public, max-age=3600
Une réponse 304 Not Modified ne contient pas de nouveau corps de représentation. Elle indique à un client possédant la réponse correspondante en cache qu’il peut utiliser cette copie. Si la représentation actuelle du serveur est différente, vous recevrez normalement une réponse 200 avec le nouveau contenu.
Ne vous attendez pas à recevoir un 304 simplement en copiant cet exemple. Il faut une ressource qui envoie réellement des ETags, un validateur correspondant et des serveurs et intermédiaires qui respectent ce mécanisme. Certains serveurs utilisent plutôt Last-Modified et If-Modified-Since. Le guide MDN sur les requêtes conditionnelles explique les deux approches.
Étape 4 : vérifier que le cache fournit la bonne variante
Une réponse peut varier selon d’autres critères que son URL, notamment la compression ou la langue. Par exemple, un serveur qui sélectionne une réponse selon l’en-tête de requête Accept-Language peut avoir besoin d’un en-tête de réponse Vary: Accept-Language adapté aux caches qui stockent ces variantes.
Si votre site vient d’ajouter une langue, testez les URL localisées ainsi que les réponses dont la langue est négociée.
Dans le générateur cURL, vous pouvez ajouter un en-tête personnalisé comme celui-ci :
Accept-Language: fr
Comparez ensuite le résultat avec une requête en anglais si le serveur est conçu pour négocier la langue de cette manière. De nombreux sites multilingues utilisent plutôt des URL distinctes pour chaque langue ; l’en-tête peut alors n’avoir aucun effet. Ne supposez pas qu’un en-tête modifie le contenu simplement parce que votre client l’a envoyé.
Étape 5 : regarder au-delà du serveur d’origine
Si cURL reçoit le CSS actuel alors que le navigateur affiche l’ancien, examinez l’URL exacte chargée par le navigateur. La page peut référencer un ancien nom de fichier, un service worker peut renvoyer une ressource en cache, ou un CDN peut encore servir une réponse stockée pour un chemin particulier.
Ouvrez le panneau Réseau, rechargez la page et comparez l’URL de la requête et les en-têtes de réponse. Le guide Web2Generators pour examiner les requêtes réseau d’un site décrit la procédure dans Chrome, Firefox, Edge et Safari.
L’option “Désactiver le cache” du navigateur, lorsqu’elle est disponible, aide à isoler le comportement du cache HTTP du navigateur pendant que les outils de développement sont ouverts. Elle ne contourne pas nécessairement un CDN, un service worker, un cache applicatif ou un cache côté serveur. C’est un test parmi d’autres, pas une conclusion.
Étape 6 : choisir une politique de cache selon le contenu
Les compromis dépendent des ressources :
Fichiers statiques versionnés. Un fichier CSS comme app.7f31a2.css, dont le nom change dès que le contenu change, peut souvent avoir une longue durée de fraîcheur. Assurez-vous que le déploiement met à jour les références HTML vers le nouveau nom. Un nom fondé sur le contenu évite de demander aux caches de deviner si une URL inchangée contient une nouvelle version.
Documents HTML. Ils ont souvent besoin d’une durée de fraîcheur plus courte ou d’une revalidation pour que les visiteurs découvrent les nouvelles références de ressources après un déploiement. La politique adaptée dépend du site et de son hébergement.
Réponses personnelles ou sensibles. Ne supposez jamais qu’une réponse API peut être partagée entre les caches simplement parce qu’elle est rapide à récupérer. L’authentification, les représentations propres à un utilisateur, les cookies et la confidentialité exigent une politique de cache réfléchie. Appliquez les contrôles adaptés et évitez de copier des jetons ou des données personnelles dans les commandes de diagnostic ou les journaux.
La mise en cache répond autant aux besoins du service qu’à ceux de la performance. Un prix, une page de compte ou une notification de sécurité n’ont pas les mêmes exigences de fraîcheur qu’une icône immuable.
Un diagnostic en cinq minutes
Lorsqu’une personne signale une ancienne page, recueillez l’URL exacte et l’heure approximative. Récupérez les en-têtes avec cURL. Notez le statut, Cache-Control, le validateur et les éventuels indices d’un intermédiaire. Vérifiez que le navigateur a demandé la même URL. Effectuez ensuite une requête conditionnelle avec un véritable ETag, s’il est disponible.
Si le navigateur et le terminal donnent des résultats différents, examinez les couches entre eux au lieu de traiter “le cache” comme un seul système. Si cURL et le navigateur renvoient tous deux une ancienne version, vérifiez le déploiement et les caches partagés placés devant lui. Une requête cURL vers l’URL publique ne prouve pas que vous avez atteint directement l’origine.
Questions fréquentes
Un statut 304 est-il une erreur ?
Non. Pour un GET ou un HEAD conditionnel, 304 Not Modified indique normalement une validation réussie : le client doit réutiliser la représentation qu’il possède déjà. Ce n’est pas une panne du serveur.
Ajouter des paramètres à l’URL corrige-t-il toujours un CSS obsolète ?
Changer l’URL peut créer une clé de cache distincte, mais des paramètres aléatoires sont un contournement de diagnostic, pas une stratégie de publication complète. Des noms de fichiers versionnés et des en-têtes cohérents sont généralement plus faciles à maintenir.
Le générateur cURL contourne-t-il les caches ?
Non. Il construit des commandes HTTP. Lorsque vous en exécutez une, la requête peut toujours traverser des proxys inverses, des CDN et d’autres intermédiaires. Examinez les en-têtes renvoyés et la destination réelle.
Testez l’URL qui fournit un ancien contenu
Ouvrez le générateur gratuit de commandes cURL, saisissez l’URL exacte relevée dans le panneau Réseau de votre navigateur et activez Sortie détaillée dans les Options avancées. Copiez la commande dans votre terminal et comparez les en-têtes de réponse. Si la réponse contient un ETag, ajoutez-le comme en-tête personnalisé If-None-Match pour une seconde requête. Vous disposez maintenant d’une vérification reproductible à refaire après votre prochain déploiement.