Monter son propre serveur CDN : récupérer une fois, livrer mille fois
Un serveur CDN récupère chaque fichier une fois depuis l'origine, puis le livre mille fois lui-même. Le serveur d'origine ne voit plus qu'une fraction des requêtes, et les visiteurs obtiennent des temps de chargement courts. Voici comment en monter un.
Chaque fichier qu'un visiteur charge depuis votre site, votre serveur d'origine le récupère dans le système de fichiers, l'emballe dans une réponse et l'envoie. Avec dix visiteurs par jour, personne ne le remarque. Avec dix mille visiteurs simultanés, c'est exactement la raison pour laquelle le site devient lent, alors que l'application proprement dite n'a rien à faire.
Un serveur CDN se place devant. Il récupère chaque fichier une fois depuis l'origine et le livre ensuite lui-même, aussi souvent qu'il est demandé. L'origine ne voit de ce fait plus qu'une fraction des requêtes, et les visiteurs reçoivent leurs fichiers d'un serveur qui ne fait rien d'autre que livrer. Cet article montre ce que cela apporte vraiment, comment monter un tel serveur avec nginx et ce qui compte en matière de trafic et d'emplacement.
Ce que fait réellement un serveur CDN
Le déroulement se raconte en trois phrases :
- Un visiteur demande un fichier au serveur CDN.
- S'il est déjà là, il part aussitôt. C'est un hit.
- S'il n'est pas là, le serveur CDN le récupère à l'origine, le dépose et le livre. C'est un miss, et il ne se produit qu'une fois par fichier et par durée de validité.
Le principe est le même que pour un serveur de streaming : récupérer une fois, livrer de multiples fois. La seule différence est qu'un serveur de streaming distribue un flux de données en cours et un serveur CDN des fichiers terminés.
Le calcul : de combien un cache soulage
Le taux de hits est décisif, c'est-à-dire la part des requêtes auxquelles le cache répond lui-même. Pour un million de requêtes par jour, le soulagement ressemble à ceci :
| Taux de hits | Requêtes à l'origine | Soulagement |
|---|---|---|
| 50 % | 500 000 | facteur 2 |
| 80 % | 200 000 | facteur 5 |
| 95 % | 50 000 | facteur 20 |
| 99 % | 10 000 | facteur 100 |
Pour les fichiers statiques, 95 pour cent et plus sont le cas normal, car les images, les scripts et les polices changent rarement. Le saut de 95 à 99 pour cent est d'ailleurs plus précieux que les chiffres ne le laissent supposer : il divise encore par cinq la charge restante.
En même temps, le trafic se déplace. L'origine ne transfère presque plus rien, le serveur CDN d'autant plus. Pour 500 000 appels d'images par jour à 400 Ko chacun, cela fait 200 Go par jour, soit environ 6 To par mois, rien que pour les images. C'est pourquoi, sur un nœud CDN, le chiffre important n'est pas le CPU, mais la connectivité et le volume.
Quand un serveur CDN à soi vaut la peine
Les prestataires CDN commerciaux facturent au gigaoctet transféré. C'est commode et peu coûteux quand le volume est faible. À partir d'une certaine quantité, le rapport s'inverse :
| Cas | Serveur CDN à soi | Facturation au gigaoctet |
|---|---|---|
| Quelques Go par mois | ne vaut guère la peine | moins cher |
| Plusieurs To par mois | prix mensuel fixe | croît linéairement |
| Gros téléchargements, archives vidéo | nettement avantageux | devient vite coûteux |
| Contrôle total sur les règles et les protocoles | complet | limité |
S'y ajoute un point difficile à chiffrer : avec un nœud à vous, vous décidez vous-même de ce qui est mis en cache, pour combien de temps, et quand cela disparaît. Vous n'êtes pas lié aux règles d'un prestataire extérieur et vous pouvez aussi livrer des choses qui n'entrent pas dans des schémas imposés.
Montage avec nginx comme cache
Installer
apt update
apt install -y nginx
Il n'en faut pas plus, le cache est intégré à nginx. Créez le répertoire dans lequel les fichiers doivent se trouver :
mkdir -p /var/cache/cdn
chown www-data:www-data /var/cache/cdn
Configurer
Le cache est décrit une fois dans le bloc http, puis utilisé dans les différents emplacements :
proxy_cache_path /var/cache/cdn
levels=1:2
keys_zone=cdn:100m
max_size=200g
inactive=30d
use_temp_path=off;
proxy_cache_key "$scheme$request_method$host$request_uri";
Les valeurs signifient, dans le détail : keys_zone=cdn:100m suffit pour environ 800 000 entrées, max_size limite la place sur le disque, inactive=30d jette les fichiers que personne n'a demandés depuis 30 jours, et use_temp_path=off évite un déplacement inutile entre répertoires.
En dessous, le serveur proprement dit :
server {
listen 443 ssl;
http2 on;
server_name cdn.example.com;
ssl_certificate /etc/letsencrypt/live/cdn.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cdn.example.com/privkey.pem;
location / {
proxy_pass https://origin.example.com;
proxy_set_header Host origin.example.com;
proxy_http_version 1.1;
proxy_cache cdn;
proxy_cache_valid 200 301 302 24h;
proxy_cache_valid 404 1m;
# regrouper plusieurs miss simultanés en une seule requête
proxy_cache_lock on;
proxy_cache_lock_timeout 10s;
# continuer à servir en cas de problème à l'origine
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
# les cookies de session ne doivent pas faire exploser le cache
proxy_ignore_headers Set-Cookie;
proxy_hide_header Set-Cookie;
add_header X-Cache-Status $upstream_cache_status always;
}
}
Trois lignes méritent une attention particulière. proxy_cache_lock veille à ce que, pour un fichier nouveau, cent visiteurs n'interrogent pas l'origine en même temps, mais un seul, les autres attendant. proxy_cache_use_stale maintient la livraison pendant que l'origine redémarre ou répond lentement. Et le masquage de Set-Cookie évite qu'une seule réponse portant un cookie de session rende tout le cache inutilisable.
Vérifier que cela prend
nginx -t && systemctl reload nginx
curl -sI https://cdn.example.com/bild.jpg | grep -i x-cache-status
curl -sI https://cdn.example.com/bild.jpg | grep -i x-cache-status
Au premier appel, il y a MISS, au second HIT. Si MISS apparaît aussi la deuxième fois, cela vient presque toujours de l'un des trois suspects habituels : un Set-Cookie dans la réponse, un Cache-Control: no-store venant de l'origine ou une indication de longueur manquante.
Ce qui est mis en cache et ce qui ne l'est pas
La solution propre est que l'origine le dise elle-même. Elle connaît ses contenus, le cache non. Pour cela, elle pose les en-têtes adéquats :
| Contenu | En-tête à l'origine |
|---|---|
| Fichier avec somme de contrôle dans le nom | Cache-Control: public, max-age=31536000, immutable |
| Image ou téléchargement sans versionnage | Cache-Control: public, max-age=86400 |
| HTML d'une page publique | Cache-Control: public, max-age=300 |
| Espace connecté, panier | Cache-Control: private, no-store |
Si vous voulez décider dans le cache lui-même, cela passe par des emplacements propres à chaque type de fichier :
location ~* \.(jpg|jpeg|png|gif|webp|avif|svg|ico|css|js|woff2|mp4|zip)$ {
proxy_pass https://origin.example.com;
proxy_cache cdn;
proxy_cache_valid 200 7d;
add_header Cache-Control "public, max-age=604800";
add_header X-Cache-Status $upstream_cache_status always;
}
Se débarrasser de contenus précis
Le chemin le plus simple est de ne pas avoir à supprimer : qui donne un nouveau nom de fichier à chaque image modifiée, par exemple logo.a1b2c3.png, n'a pas le problème. L'ancien fichier sort tout seul du cache à un moment ou un autre.
Si ce n'est pas possible, nginx propose le module libre ngx_cache_purge. Sans module supplémentaire, le chemin se calcule à partir de la clé de cache, parce que nginx utilise sa somme MD5 comme nom de fichier :
KEY="httpsGETcdn.example.com/bild.jpg"
H=$(printf '%s' "$KEY" | md5sum | cut -d' ' -f1)
rm -f "/var/cache/cdn/${H: -1}/${H: -3:2}/$H"
Pour le quotidien, un petit script qui exécute ces lignes pour une liste de chemins suffit la plupart du temps.
Gros fichiers et vidéos
Pour les fichiers de plus de quelques centaines de mégaoctets, vous avez besoin du découpage en blocs, sinon le cache recharge tout le fichier à chaque saut dans la vidéo :
location /video/ {
proxy_pass https://origin.example.com;
proxy_cache cdn;
proxy_cache_valid 200 206 7d;
slice 1m;
proxy_set_header Range $slice_range;
proxy_cache_key "$uri$slice_range";
}
nginx ne récupère ainsi que les blocs réellement demandés. Si un spectateur saute au milieu d'un film, quelques mégaoctets passent par la ligne au lieu de plusieurs gigaoctets.
TLS, HTTP/2 et HTTP/3
Un nœud CDN livre beaucoup de petits fichiers à de nombreuses connexions, c'est exactement là que les protocoles récents sont payants. HTTP/2 est réglé avec http2 on;. HTTP/3 apporte surtout quelque chose sur les connexions mobiles, parce qu'un changement de réseau ne fait pas tomber la connexion :
listen 443 quic reuseport;
listen 443 ssl;
http2 on;
add_header Alt-Svc 'h3=":443"; ma=86400';
N'oubliez pas d'ouvrir l'UDP 443 dans le pare-feu. C'est la raison la plus fréquente pour laquelle HTTP/3 est configuré et que personne ne l'utilise pour autant.
Plusieurs emplacements
Un seul nœud apporte déjà la plus grande part du gain, à savoir le soulagement de l'origine. Le deuxième gain, des chemins courts vers le visiteur, ne vient qu'avec plusieurs emplacements.
La répartition passe par le DNS : le même nom pointe, selon la provenance du visiteur, vers un nœud différent. Il vous faut pour cela un fournisseur DNS avec attribution géographique. Tous les nœuds reçoivent la même configuration et la même origine, ils ne savent rien l'un de l'autre, et c'est précisément ce qui rend le montage si robuste : si un nœud tombe, vous le retirez du DNS, et les autres prennent le relais.
KernelHost propose des emplacements en Allemagne, sur la côte ouest et sur la côte est des États-Unis ainsi qu'à Singapour et en Corée du Sud. Vous trouverez un aperçu avec les latences sous Emplacements des serveurs. Comme règle empirique : un nœud par continent sur lequel se trouve une part notable de votre public.
Mesurer au lieu de deviner
Écrivez le statut du cache dans le journal, vous pourrez alors compter le taux de hits à tout moment :
log_format cdn '$remote_addr $status $body_bytes_sent '
'$upstream_cache_status "$request"';
access_log /var/log/nginx/cdn.log cdn;
L'analyse se fait avec une seule ligne :
awk '{print $4}' /var/log/nginx/cdn.log | sort | uniq -c | sort -rn
Si la part de HIT sur les fichiers statiques est sous 90 pour cent, chercher la cause vaut presque toujours la peine : ce sont le plus souvent des cookies dans les réponses, des durées de validité trop courtes ou des requêtes aux paramètres changeants en fin d'adresse, qui ressemblent chaque fois à un nouveau fichier pour le cache.
Quel serveur convient
Un nœud CDN est une machine à livrer. L'ordre des propriétés importantes est donc différent de celui d'un serveur applicatif :
- Trafic et connectivité. C'est là que naît toute la charge. En exploitation, un nœud transfère régulièrement plusieurs téraoctets par mois.
- Disque. Le NVMe est nettement avantageux, parce que le cache se compose de nombreux petits accès aléatoires. La taille se règle sur la part de votre contenu qui est chaude, et non sur la taille totale du contenu.
- RAM. Le système d'exploitation utilise la mémoire libre comme deuxième cache devant le disque. Plus de RAM signifie ici directement plus de vitesse.
- CPU. Le moins critique, il ne sert que pour TLS, et les processeurs modernes s'en occupent accessoirement.
Nos serveurs root à trafic illimité sont faits exactement pour ce cas : de vrais 2 × 1 Gbit/s par serveur sans bridage, un volume illimité au prix mensuel fixe, du NVMe datacenter en RAID et 3,2 Tbps de protection anti-flood d'origine. La mise à disposition prend moins de 30 secondes, avec un accès root complet, une IPv4 et un réseau IPv6 en /64. Comme la facturation se fait en prépaiement et reste résiliable chaque mois, vous pouvez commencer avec un nœud et ajouter un deuxième emplacement à tout moment, quand votre public se répartit.
Questions fréquentes
Que fait exactement un serveur CDN ?
Quel est le soulagement réel ?
Ai-je besoin d'un prestataire commercial pour cela ?
Quels fichiers mettre en cache et lesquels non ?
Combien de temps le cache doit-il garder les fichiers ?
Comment supprimer un fichier du cache ?
Que se passe-t-il si le serveur d'origine tombe en panne ?
Cela fonctionne-t-il aussi pour de gros fichiers vidéo ?
De combien d'emplacements ai-je besoin ?
Comment savoir que le cache fonctionne ?
2026 KernelHost GmbH. Tous droits réservés. Ce guide est protégé par le droit d'auteur. Sa republication sur d'autres sites web, même partielle ou sous une forme modifiée, n'est pas autorisée sans notre accord écrit. Les citations accompagnées de la source et d'un lien sont expressément les bienvenues.

