Ce qu’un CDN fait réellement à une requête

Un utilisateur à Tokyo qui charge une page hébergée sur un serveur en Virginie paie cette distance deux fois : une fois pour que la requête arrive, une fois pour que la réponse revienne. La fibre optique transporte les données à une fraction fixe de la vitesse de la lumière, et ce seul trajet coûte déjà 150 à 200 ms d’aller-retour avant même que l’origine n’ait commencé à travailler. Ajoutez une requête en base de données et un ou deux sauts réseau lents, et une page qui semble instantanée juste à côté devient poussive à l’autre bout de la planète.

Un CDN, ou content delivery network, résout ce problème pour tout contenu qui peut être copié : images, CSS, bundles JavaScript, segments vidéo, parfois des pages HTML entières. Un réseau de serveurs positionnés près des utilisateurs garde des copies de ce contenu et répond directement, si bien que la plupart des requêtes ne traversent jamais l’océan. L’origine reste la version faisant autorité. Le CDN se place devant elle et intercepte autant de requêtes que possible pour les servir depuis une copie.

Comment une requête choisit quel serveur répond

Un opérateur de CDN fait tourner des serveurs dans de nombreux emplacements physiques, généralement appelés Points de Présence, ou PoP : des data centers dans ou près des grandes villes, chacun détenant une partie du contenu mis en cache et une voie rapide vers l’origine pour le reste. Deux mécanismes décident quel PoP traite une requête donnée, et les grands CDN utilisent les deux.

Le routage basé sur le DNS résout le même nom d’hôte vers des adresses IP différentes selon l’origine de la requête. Une requête DNS pour assets.example.com depuis un résolveur à Francfort récupère l’IP d’un PoP à Francfort ; la même requête depuis São Paulo récupère une IP entièrement différente.

L’anycast va plus loin. Plusieurs PoP annoncent la même adresse IP via BGP, et le routage internet ordinaire décide lequel reçoit effectivement un paquet donné, en fonction de la topologie du réseau plutôt que de la géographie. Le CDN ne fait jamais ce choix lui-même. Le failover est aussi plus rapide : quand un PoP disparaît du réseau, les routeurs cessent de lui envoyer du trafic, sans aucun changement DNS à propager.

1
2
3
4
5
6
client (Tokyo)  ---DNS/anycast--->  PoP le plus proche  ---cache hit--->  réponse
                                          |
                                    cache miss
                                          |
                                          v
                                   origine (Virginie)

Dans les deux cas, le client ne parle jamais directement à l’origine pour du contenu mis en cache. Il parle au PoP que la couche de routage a choisi, et ce PoP a soit le contenu, soit va le chercher une fois et s’en souvient. Chaque PoP fait le travail d’un reverse proxy devant l’origine : il termine la connexion du client et décide de ce qui se passe ensuite, avec un cache attaché et des milliers de copies tournant en même temps à travers le globe.

Cache hit ou cache miss : comment le savoir

La première requête pour une URL via un PoP donné est un cache miss. Le PoP n’a rien en stock, donc il va la chercher sur l’origine, stocke une copie selon les règles de la réponse, et la retourne. Chaque requête suivante pour cette URL, depuis n’importe quel client routé vers le même PoP, est un hit : servie depuis le stockage du PoP, sans passer par l’origine, jusqu’à ce que la copie expire ou soit purgée.

Une seule commande vous dit si c’était un hit ou un miss :

1
curl -I https://assets.example.com/app.css

Une réponse mise en cache porte un en-tête propriétaire qui nomme le résultat. Cloudflare ajoute CF-Cache-Status avec des valeurs comme HIT, MISS, EXPIRED (le TTL est arrivé à son terme, donc le PoP a récupéré une nouvelle copie), REVALIDATED (l’origine a confirmé que l’ancienne copie était toujours valide), ou DYNAMIC (le CDN a décidé que cette réponse n’est pas cacheable du tout). L’équivalent chez Fastly est X-Cache: HIT ou X-Cache: MISS. Exécutez deux fois la même requête sur une URL cacheable et la seconde devrait passer de MISS à HIT.

1
2
3
4
5
HTTP/2 200
cache-control: public, max-age=3600
cf-cache-status: HIT
age: 412
content-type: text/css

age indique combien de secondes se sont écoulées depuis que le PoP a récupéré cette copie sur l’origine. Ici, 412 secondes se sont écoulées sur un TTL de 3600 secondes, donc la copie a encore près d’une heure avant que la prochaine requête ne déclenche une nouvelle récupération.

Comment Cache-Control indique au CDN quoi mettre en cache

Le CDN ne devine pas ce qu’il faut mettre en cache. L’origine le lui dit, via l’en-tête Cache-Control sur chaque réponse.

DirectiveCe qu’elle fait
max-age=NCombien de temps un cache (navigateur ou CDN) peut garder la réponse, en secondes
s-maxage=NCombien de temps un cache partagé (le CDN) peut la garder — remplace max-age spécifiquement pour le CDN ; les navigateurs continuent de suivre max-age
publicAutorise explicitement la mise en cache, même sur des réponses qui sembleraient sinon privées (par exemple avec un en-tête Authorization)
privateSeul le navigateur peut mettre ceci en cache ; le CDN ne doit pas le faire
no-cacheLes caches ont le droit de le stocker mais doivent revalider auprès de l’origine avant de le resservir
no-storeNe jamais mettre ceci en cache, ni navigateur ni CDN

Pour un asset statique au nom de fichier haché (app.a3f9c1.css), le réglage habituel est agressif : Cache-Control: public, max-age=31536000, immutable. Le nom de fichier change dès que le contenu change, donc un an ne pose aucun risque. Une copie périmée de app.a3f9c1.css est une contradiction en soi, puisque ce nom exact ne pointe jamais que vers une seule version du fichier.

Pour du HTML ou une réponse d’API que vous voulez mettre en cache tout en la gardant fraîche, Cache-Control: public, s-maxage=60, max-age=0 la garde au niveau du CDN pendant une minute pendant que les navigateurs revalident à chaque fois. C’est le même réflexe de TTL court en cache-aside qui sous-tend le fonctionnement du cache Redis, appliqué ici par un en-tête HTTP plutôt que par du code applicatif.

Inversez ce réglage et vous créez une fuite. Servir une page propre à un utilisateur avec Cache-Control: public, max-age=3600 et sans Vary sur le cookie de session signifie que le CDN peut renvoyer la page mise en cache de l’utilisateur A à l’utilisateur B.

Comment purger un cache CDN périmé

Parfois un TTL ne suffit pas : vous avez déployé un correctif et vous ne pouvez pas attendre une heure qu’il atteigne tout le monde. Chaque CDN expose une API de purge ou une action depuis son dashboard pour ça. Dites-lui qu’une URL, ou une zone entière, n’est plus valide, et la prochaine requête sera traitée comme un cache miss, quel que soit le TTL restant.

L’habitude la moins coûteuse est de ne jamais avoir besoin de purger. Versionnez le nom de fichier avec un hash de contenu ou un paramètre de requête incrémenté, pour que chaque déploiement soit une nouvelle URL avec sa propre entrée de cache, et que l’ancienne URL cesse simplement d’être demandée. Gardez la purge pour ce que vous ne pouvez pas contourner : un incident, un retrait légal, un cache empoisonné par un en-tête Vary mal configuré.

Quand un CDN ne sert à rien, et ce qu’il coûte

Les réponses personnalisées ne sont pas cacheables, dans aucun sens utile. Un dashboard privé, une page de panier, tout ce qui est lié à une session — la requête suivante voudra forcément un contenu différent. Pointer un CDN vers ces endpoints n’apporte rien, sinon la fausse impression d’avoir ajouté du cache, et si les règles de cache sont mal réglées, ça risque activement de servir la réponse privée d’un utilisateur à un autre.

Un CDN ajoute aussi une couche à déboguer. « Ça marche en local mais pas en production » signifie parfois que le code est correct et qu’un PoP sert une réponse mise en cache avant votre correctif ; la requête n’atteint l’origine qu’une fois le TTL épuisé ou après une purge.

La forme du trafic compte aussi. Un outil interne, ou un service régional sans audience internationale, paie pour des PoP qu’il n’utilise jamais, pour résoudre un problème de latence qu’il n’a jamais eu. Un CDN résout deux problèmes précis : le trajet vers des utilisateurs distants, et la charge répétée sur l’origine. Si aucun des deux n’est votre cas, c’est une infrastructure qui n’a rien à faire ici.

Comment savoir si vous avez déjà un CDN

Exécutez curl -I sur un asset statique d’un site que vous gérez. Si la réponse porte un cache-control, un en-tête age, ou un statut propriétaire comme CF-Cache-Status ou X-Cache, quelque chose met déjà en cache devant votre origine, que vous l’ayez mis en place volontairement ou que votre hébergeur l’ait inclus d’office. Si ces en-têtes sont absents sur des assets demandés depuis plus d’une région, voilà le cas concret pour en ajouter un : Cloudflare, AWS CloudFront et Fastly proposent tous une formule gratuite ou à l’usage qui met un domaine derrière un CDN en moins d’une heure, sans que le code de l’origine ait à changer.

Articles associés