Éviter les requêtes réseau inutiles avec le cache HTTP

La récupération de ressources sur le réseau est à la fois lente et coûteuse:

  • Les réponses volumineuses nécessitent de nombreux aller-retour entre le navigateur et le serveur.
  • Votre page ne se chargera pas tant que toutes ses ressources essentielles n'auront pas été entièrement téléchargées.
  • Si un utilisateur accède à votre site avec un forfait de données mobiles limité, chaque requête réseau inutile est un gaspillage d'argent.

Comment éviter les requêtes réseau inutiles ? Le cache HTTP du navigateur est votre première ligne de défense. Il ne s'agit pas nécessairement de l'approche la plus puissante ni la plus flexible, et vous avez un contrôle limité sur la durée de vie des réponses mises en cache. Toutefois, cette approche est efficace, compatible avec tous les navigateurs et ne nécessite pas beaucoup d'efforts.

Ce guide présente les principes de base d'une implémentation efficace de la mise en cache HTTP.

Compatibilité du navigateur

Il n'existe en fait pas d'API appelée "cache HTTP". Il s'agit du nom général d'un ensemble d'API de plate-forme Web. Ces API sont compatibles avec tous les navigateurs:

Cache-Control

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

ETag

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

Last-Modified

Browser Support

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 1.
  • Safari: 1.

Source

Fonctionnement du cache HTTP

Toutes les requêtes HTTP envoyées par le navigateur sont d'abord acheminées vers le cache du navigateur pour vérifier s'il existe une réponse mise en cache valide pouvant être utilisée pour répondre à la requête. En cas de correspondance, la réponse est lue à partir du cache, ce qui élimine à la fois la latence du réseau et les coûts de données associés au transfert.

Le comportement du cache HTTP est contrôlé par une combinaison d'en-têtes de requête et d'en-têtes de réponse. Dans un scénario idéal, vous contrôlez à la fois le code de votre application Web (qui déterminera les en-têtes de requête) et la configuration de votre serveur Web (qui déterminera les en-têtes de réponse).

Pour une présentation plus détaillée du concept, consultez l'article Mise en cache HTTP de MDN.

En-têtes de requête: utilisez les valeurs par défaut (généralement)

Un certain nombre d'en-têtes importants doivent être inclus dans les requêtes sortantes de votre application Web, mais le navigateur s'en charge presque toujours en votre nom lorsqu'il envoie des requêtes. Les en-têtes de requête qui affectent la vérification de la fraîcheur, comme If-None-Match et If-Modified-Since, apparaissent en fonction de la compréhension du navigateur des valeurs actuelles du cache HTTP.

C'est une bonne nouvelle : cela signifie que vous pouvez continuer à inclure des balises telles que <img src="my-image.png"> dans votre code HTML, et le navigateur s'occupe automatiquement de la mise en cache HTTP pour vous, sans effort supplémentaire.

En-têtes de réponse: configurer votre serveur Web

La partie la plus importante de la configuration de la mise en cache HTTP est les en-têtes que votre serveur Web ajoute à chaque réponse sortante. Les en-têtes suivants sont tous pris en compte dans le comportement de mise en cache efficace:

  • Cache-Control : le serveur peut renvoyer une directive Cache-Control pour spécifier comment et pendant combien de temps le navigateur et les autres caches intermédiaires doivent mettre en cache la réponse individuelle.
  • ETag : lorsque le navigateur trouve une réponse mise en cache qui a expiré, il peut envoyer un petit jeton (généralement un hachage du contenu du fichier) au serveur pour vérifier si le fichier a changé. Si le serveur renvoie le même jeton, le fichier est identique et il n'est pas nécessaire de le télécharger à nouveau.
  • Last-Modified : cet en-tête a la même fonction que ETag, mais utilise une stratégie basée sur le temps pour déterminer si une ressource a changé, contrairement à la stratégie basée sur le contenu de ETag.

Certains serveurs Web sont compatibles avec la configuration de ces en-têtes par défaut, tandis que d'autres les ignorent complètement, sauf si vous les configurez explicitement. La méthode de configuration des en-têtes varie considérablement selon le serveur Web que vous utilisez. Nous vous recommandons de consulter la documentation de votre serveur pour obtenir les informations les plus précises.

Pour vous éviter de chercher, voici des instructions pour configurer quelques serveurs Web populaires:

Omettre l'en-tête de réponse Cache-Control ne désactive pas le cache HTTP. Au lieu de cela, les navigateurs devinent le type de comportement de mise en cache le plus approprié pour un type de contenu donné. Vous voudrez probablement avoir plus de contrôle que cela. Prenez donc le temps de configurer vos en-têtes de réponse.

Quelles valeurs d'en-tête de réponse devez-vous utiliser ?

Lorsque vous configurez les en-têtes de réponse de votre serveur Web, vous devez tenir compte de deux scénarios importants.

Mise en cache de longue durée pour les URL avec versions gérées

Supposons que votre serveur demande aux navigateurs de mettre en cache un fichier CSS pendant un an (Cache-Control: max-age=31536000), mais que votre concepteur vient de publier une mise à jour d'urgence que vous devez déployer immédiatement. Comment avertir les navigateurs de mettre à jour la copie mise en cache "obsolète" du fichier ? Non, du moins pas sans modifier l'URL de la ressource.

Une fois que le navigateur a mis en cache la réponse, la version mise en cache est utilisée jusqu'à ce qu'elle ne soit plus à jour, comme déterminé par max-age ou expires, ou jusqu'à ce qu'elle soit supprimée du cache pour une autre raison (par exemple, lorsque l'utilisateur vide le cache de son navigateur). Par conséquent, différents utilisateurs peuvent utiliser différentes versions du fichier lors de la création de la page: les utilisateurs qui viennent d'extraire la ressource utilisent la nouvelle version, tandis que les utilisateurs qui ont mis en cache une copie antérieure (mais toujours valide) utilisent une ancienne version de sa réponse.

Comment profiter du meilleur des deux mondes: le cache côté client et les mises à jour rapides ? Vous modifiez l'URL de la ressource et forcez l'utilisateur à télécharger la nouvelle réponse chaque fois que son contenu change. En règle générale, vous devez intégrer une empreinte du fichier ou un numéro de version dans son nom de fichier (par exemple, style.x234dff.css).

Lorsque vous répondez à des requêtes d'URL contenant une empreinte ou des informations de gestion des versions, et dont le contenu ne doit jamais changer, ajoutez Cache-Control: max-age=31536000 à vos réponses.

Cette valeur indique au navigateur que lorsqu'il doit charger la même URL à tout moment au cours de l'année suivante (31 536 000 secondes, valeur maximale prise en charge), il peut immédiatement utiliser la valeur dans le cache HTTP, sans avoir à effectuer une requête réseau auprès de votre serveur Web. C'est super ! Vous bénéficiez immédiatement de la fiabilité et de la rapidité qu'offre l'évitement du réseau.

Les outils de compilation tels que webpack peuvent automatiser le processus d'attribution d'empreintes de hachage aux URL de vos composants.

Révalidation du serveur pour les URL sans version

Malheureusement, toutes les URL que vous chargez ne sont pas versionnées. Il est possible que vous ne puissiez pas inclure d'étape de compilation avant de déployer votre application Web. Vous ne pouvez donc pas ajouter de hachages aux URL de vos composants. De plus, chaque application Web a besoin de fichiers HTML. Ces fichiers n'incluront (presque) jamais d'informations de gestion des versions, car personne ne prendra la peine d'utiliser votre application Web s'il doit se souvenir que l'URL à visiter est https://example.com/index.34def12.html. Que pouvez-vous faire pour ces URL ?

C'est un scénario dans lequel vous devez admettre votre défaite. La mise en cache HTTP n'est pas assez efficace pour éviter complètement le réseau. (Ne vous inquiétez pas, vous allez bientôt découvrir les service workers, qui vous aideront à reprendre l'avantage.) Toutefois, vous pouvez prendre quelques mesures pour vous assurer que les requêtes réseau sont aussi rapides et efficaces que possible.

Les valeurs Cache-Control suivantes peuvent vous aider à affiner l'emplacement et la méthode de mise en cache des URL non versionnées:

  • no-cache. Cela indique au navigateur qu'il doit effectuer une nouvelle validation avec le serveur à chaque fois avant d'utiliser une version mise en cache de l'URL.
  • no-store. Cela indique au navigateur et aux autres caches intermédiaires (comme les CDN) de ne jamais stocker aucune version du fichier.
  • private. Les navigateurs peuvent mettre en cache le fichier, mais les caches intermédiaires ne le peuvent pas.
  • public. La réponse peut être stockée par n'importe quel cache.

Consultez l'annexe: organigramme Cache-Control pour visualiser le processus de sélection des valeurs Cache-Control à utiliser. Cache-Control peut également accepter une liste de directives séparées par une virgule. Consultez l'annexe: Exemples Cache-Control.

Vous pouvez également définir ETag ou Last-Modified. Comme indiqué dans la section En-têtes de réponse, ETag et Last-Modified ont tous deux le même objectif: déterminer si le navigateur doit télécharger à nouveau un fichier mis en cache qui a expiré. Nous vous recommandons d'utiliser ETag, car il est plus précis.

Supposons que 120 secondes se soient écoulées depuis l'extraction initiale et que le navigateur ait lancé une nouvelle requête pour la même ressource. Tout d'abord, le navigateur vérifie le cache HTTP et trouve la réponse précédente. Malheureusement, le navigateur ne peut pas utiliser la réponse précédente, car elle a expiré. À ce stade, le navigateur peut envoyer une nouvelle requête et récupérer la nouvelle réponse complète. Toutefois, cette approche est inefficace, car si la ressource n'a pas changé, il n'y a aucune raison de télécharger les mêmes informations qui se trouvent déjà dans le cache.

C'est le problème que les jetons de validation, comme indiqué dans l'en-tête ETag, sont conçus pour résoudre. Le serveur génère et renvoie un jeton arbitraire, qui est généralement un hachage ou une autre empreinte du contenu du fichier. Le navigateur n'a pas besoin de savoir comment l'empreinte est générée. Il doit simplement l'envoyer au serveur lors de la prochaine requête. Si l'empreinte est toujours la même, la ressource n'a pas changé et le navigateur peut ignorer le téléchargement.

Définir ETag ou Last-Modified rend la requête de validation beaucoup plus efficace en lui permettant de déclencher les en-têtes de requête If-Modified-Since ou If-None-Match mentionnés dans la section En-têtes de requête.

Lorsqu'un serveur Web correctement configuré voit ces en-têtes de requête entrants, il peut vérifier si la version de la ressource que le navigateur a déjà dans son cache HTTP correspond à la dernière version sur le serveur Web. En cas de correspondance, le serveur peut répondre avec une réponse HTTP 304 Not Modified, ce qui équivaut à dire "Hey, continuez à utiliser ce que vous avez déjà !". Il y a très peu de données à transférer lors de l'envoi de ce type de réponse. Il est donc généralement beaucoup plus rapide que d'avoir à renvoyer une copie de la ressource demandée.

Visualisation d&#39;un client qui demande une ressource et du serveur qui répond avec un en-tête 304.
Le navigateur demande /file au serveur et inclut l'en-tête If-None-Match pour indiquer au serveur de ne renvoyer le fichier complet que si le ETag du fichier sur le serveur ne correspond pas à la valeur If-None-Match du navigateur. Dans ce cas, les deux valeurs correspondent. Le serveur renvoie donc une réponse 304 Not Modified avec des instructions sur la durée pendant laquelle le fichier doit être mis en cache (Cache-Control: max-age=120).

Résumé

Le cache HTTP est un moyen efficace d'améliorer les performances de chargement, car il réduit les requêtes réseau inutiles. Elle est compatible avec tous les navigateurs et ne nécessite pas beaucoup de travail pour être configurée.

Les configurations Cache-Control suivantes sont un bon point de départ:

  • Cache-Control: no-cache pour les ressources qui doivent être revalidées avec le serveur avant chaque utilisation.
  • Cache-Control: no-store pour les ressources qui ne doivent jamais être mises en cache.
  • Cache-Control: max-age=31536000 pour les ressources avec version.

L'en-tête ETag ou Last-Modified peut vous aider à valider plus efficacement les ressources de cache expirées.

En savoir plus

Si vous souhaitez aller au-delà des principes de base de l'utilisation de l'en-tête Cache-Control, consultez le guide Bonnes pratiques de mise en cache et pièges liés à la valeur max-age de Jake Archibald.

Consultez Aimez votre cache pour savoir comment optimiser l'utilisation de votre cache pour les visiteurs réguliers.

Annexe: Autres conseils

Si vous avez le temps, voici d'autres façons d'optimiser votre utilisation du cache HTTP:

  • Utilisez des URL cohérentes. Si vous diffusez le même contenu sur différentes URL, ce contenu sera extrait et stocké plusieurs fois.
  • Limitez la perte d'utilisateurs. Si une partie d'une ressource (par exemple, un fichier CSS) est mise à jour fréquemment, tandis que le reste du fichier ne l'est pas (par exemple, le code de la bibliothèque), envisagez de diviser le code fréquemment mis à jour dans un fichier distinct et d'utiliser une stratégie de mise en cache de courte durée pour le code fréquemment mis à jour et une stratégie de mise en cache de longue durée pour le code qui ne change pas souvent.
  • Consultez la nouvelle directive stale-while-revalidate si un certain degré d'obsolescence est acceptable dans votre stratégie Cache-Control.

Annexe: Organigramme Cache-Control

Organigramme
Le processus décisionnel pour définir vos en-têtes Cache-Control.

Annexe: Exemples Cache-Control

Valeur Cache-Control Explication
max-age=86400 La réponse peut être mise en cache par les navigateurs et les caches intermédiaires pendant un maximum d'un jour (60 secondes x 60 minutes x 24 heures).
private, max-age=600 La réponse peut être mise en cache par le navigateur (mais pas par les caches intermédiaires) pendant 10 minutes maximum (60 secondes x 10 minutes).
public, max-age=31536000 La réponse peut être stockée par n'importe quel cache pendant un an.
no-store La réponse ne peut pas être mise en cache et doit être récupérée intégralement à chaque requête.