Le Cache HTTP

Qu’est-ce qu’un cache et à quoi cela sert ?

Un cache correspond à un stockage local de données reçues précédemment ou plus exactement des messages de réponse HTTP reçus précédemment. Un système de cache contrôle le stockage, la récupération et la suppression de ces messages.

Grosso-modo, l’idée est la suivante : un client effectue une première requête HTTP pour par exemple récupérer une certaine ressource. Le serveur lui renvoie la ressource demandée dans sa réponse HTTP. L’agent utilisateur (le navigateur généralement) va alors pouvoir stocker cette réponse HTTP dans son cache local.

Ainsi, lorsque l’utilisateur effectuera à nouveau la même requête, l’agent utilisateur pourra immédiatement utiliser la réponse stockée dans son cache plutôt que d’envoyer la même requête à nouveau au serveur et devoir attendre la réponse.

Concrètement, le cache permet l’amélioration des performances aussi bien côté client que pour les propriétaires de site grâce à la réduction du temps de réponse et à la diminution de la consommation de la bande passante.

Le cache permet également, entre autres, de consulter certains documents ou du moins d’améliorer la consultation de certains documents hors ligne puisque la copie de la réponse du serveur est enregistrée localement dans le navigateur du visiteur (c’est-à-dire sur sa propre machine).

 

Cache privé et cache partagé

Pour bien comprendre comment fonctionne le cache, il faut avant tout savoir qu’il existe deux types de caches : les caches privés et les caches partagés.

Un cache privé est un cache qui n’appartient qu’à un seul navigateur. Typiquement, les caches des navigateurs des utilisateurs sont des caches privés puisque chaque utilisateur possède son propre historique de recherche.

Un cache partagé est un cache qui a été mis en place à un plus haut niveau et qui a vocation à être utilisé par plusieurs utilisateurs. Lorsqu’un utilisateur effectue une requête HTTP, il faut en effet savoir que la requête n’arrive pas directement au serveur : elle passe généralement par de nombreux proxies qui vont la transmettre sur le web.

Il va tout à fait être possible de configurer un cache sur ces différents proxies. En effet, un serveur intermédiaire (proxy) par lequel transite une requête HTTP peut également tout à fait utiliser un cache.

Au final, il existe une grande variété d’architectures et de configurations de caches déployées sur le World Wide Web et au sein de grandes organisations, notamment des hiérarchies nationales des caches de proxy pour économiser la bande passante transocéanique, etc.

 

Restrictions liées au cache : réponses cachable et non cachable

On dit qu’une réponse est « cachable » (« pouvant être mise en cache ») si un cache est autorisé à stocker une copie du message de réponse HTTP pour l’utiliser pour répondre aux requêtes similaires suivantes.

La forme la plus courante d’une entrée de cache est typiquement le résultat d’une requête de récupération de ressources réussie c’est-à-dire une réponse 200 (OK) à une requête GET qui contient une représentation de la ressource identifiée par la cible de la requête.

Notez qu’il est cependant également possible de mettre en cache les redirections permanentes, les résultats négatifs comme 404 (Not Found), les résultats incomplets comme 206 (Partial Content) et les réponses à des méthodes autres que GET si la définition de la méthode permet une telle mise en cache et définit quelque chose qui peut être utilisé comme clé de cache.

 

Composition d’une entrée de cache

Chaque entrée de cache se compose d’une clé de cache et d’une ou plusieurs réponses HTTP correspondant à des demandes antérieures qui utilisaient la même clé.

La clé de cache principale se compose théoriquement de la méthode de requête et de l’URI (Identifiant de Ressource Uniforme) cible. Cependant, en pratique, étant donné que les caches HTTP couramment utilisés aujourd’hui sont généralement limités à la mise en cache des réponses à GET, de nombreux caches refusent simplement d’autres méthodes et n’utilisent que l’URI comme clé de cache principale.

Notez que si la cible d’une requête fait l’objet d’une négociation de contenu, son entrée de cache peut consister en plusieurs réponses stockées chacune différenciée par une clé secondaire.

 

Durée de vie ou fraicheur d’un cache

Par définition, tout cache doit avoir une durée de vie limitée. En effet, si un cache était stocké et resservi indéfiniment par un navigateur, alors l’utilisateur n’aurait jamais accès aux changements faits ultérieurement sur le document.

La durée de vie ou de « fraicheur » d’un cache est l’un des points les plus techniques liés à celui-ci : en paramétrant une durée de vie trop courte, on se prive de l’intérêt principal du cache qui est de ne pas avoir à effectuer l’aller-retour jusqu’au serveur. Au contraire, en définissant une durée de vie trop élevée pour un cache, on court le risque que certains utilisateurs n’aient pas accès à certains changements effectués sur nos documents.

La durée de vie de la fraîcheur d’une réponse est la durée entre sa génération par le serveur d’origine et son heure d’expiration. Une heure d’expiration explicite est l’heure à laquelle le serveur d’origine prévoit qu’une réponse stockée ne peut plus être utilisée par un cache sans validation supplémentaire, tandis qu’une heure d’expiration heuristique est attribuée par une mémoire cache lorsqu’aucune heure d’expiration explicite n’est disponible.

Lorsqu’une réponse est « fraîche » dans le cache, elle peut être utilisée pour satisfaire les requêtes suivantes sans contacter le serveur d’origine, améliorant ainsi l’efficacité.

Le principal mécanisme de détermination de la fraîcheur consiste pour un serveur d’origine à fournir une heure d’expiration future explicite via un en-tête Expires ou via la directive max-age.

Pour calculer la durée de vie de fraîcheur d’une réponse, un cache va utiliser, dans cet ordre :

  • La valeur de la directive s-maxage si le cache est partagé ou ;
  • La valeur de la directive max-age ou ;
  • La valeur de l’en-tête Expires ou ;
  • Une durée de vie de fraicheur heuristique.

Une fois la durée de vie d’une réponse expirée dans le cache, celle-ci est considérée comme périmée (« stale » en anglais). Lorsqu’une réponse est périmée, le cache ne l’utilise pas immédiatement mais transmet la requête au serveur avec un en-tête If-None-Match pour vérifier si elle peut être quand même être utilisée (c’est-à-dire si aucun changement n’a été fait depuis). Si c’est le cas, le serveur renvoie une réponse avec un statut 304 (Not Modified), indiquant que la réponse cachée peut toujours être utilisée.

 

Contrôle du cache

Le champ d’en-tête Cache-Control est utilisé pour spécifier les directives pour les mécanismes de cache dans les requêtes et les réponses.

Les règles standard Cache-Control suivantes peuvent être utilisées par un client HTTP dans une requête :

  • Cache-Control: max-age : Indique la durée pendant laquelle la ressource doit être considérée comme valide (non expirée). Contrairement à expires, la durée indiquée dans cette directive commence à la date de la requête ;
  • Cache-Control: max-stale : Indique que le client accepte une réponse expirée. Une valeur optionnelle permet d’indiquer la durée maximale depuis laquelle la réponse peut être expirée mais acceptée quand même ;
  • Cache-Control: min-fresh : Indique que le client demande une réponse qui soit valide pour au moins la durée demandée (dont la date d’expiration est supérieure à la date actuelle plus la durée spécifiée) ;
  • Cache-Control: no-cache : Indique de renvoyer systématiquement la requête au serveur et ne servir une éventuelle version en cache que dans le cas où le serveur le demande ;
  • Cache-Control: no-store : La requête ou les données ne devraient jamais être mises en cache ;
  • Cache-Control: no-transform : Aucune conversion ou transformation ne devraient être réalisée sur la ressource. Ainsi, les en-tête Content-Encoding, Content-Range et Content-Type ne devraient jamais être modifiés par un proxy (serveur mandataire). Un proxy non-transparent pourrait, en l’absence de cet en-tête, convertir ou compresser (avec pertes) des images pour réduire la place occupée en cache ou diminuer le volume de données à transférer sur un lien lent ;
  • Cache-Control: only-if-cached : Indique de se limiter au contenu en cache. Dans ce cas, le client ne veut pas que sa réponse soit envoyée au serveur.

Les règles standard Cache-Control suivantes peuvent être utilisées par un serveur HTTP dans une réponse :

  • Cache-Control: must-revalidate : Le cache doit refaire une requête dans le cas où les données sont expirées afin de les mettre à jour s’il y a lieu (il reste parfaitement possible que le serveur réponde avec les mêmes données) ;
  • Cache-Control :no-cache ;
  • Cache-Control: no-store ;
  • Cache-Control: no-transform ;
  • Cache-Control: public : Indique que la réponse peut être mise en cache par n’importe quel cache ;
  • Cache-Control: private : Indique que la réponse ne doit être mise en cache que pour un utilisateur donné et ne doit donc pas être mise en cache par un cache partagé ;
  • Cache-Control: proxy-revalidate : Comme pour must-revalidate, mais force la valeur pour les caches partagés. Cette valeur est ignorée par les caches locaux.
  • Cache-Control: max-age ;
  • Cache-Control: s-maxage= : Indique une valeur pour écraser les valeurs définies par max-age ou Expires pour les caches partagés (comme les proxies). Il est donc ignoré par les caches privés (dont les navigateurs).