infra / nginx
Fichiers statiques et cache
Explication
Ce que vous allez apprendre
- Utiliser
try_filespour tester plusieurs chemins dans l'ordre avant de renvoyer une erreur - Configurer un cache long et sûr pour des assets versionnés (avec un hash dans le nom)
- Comprendre pourquoi le HTML ne doit jamais être mis en cache longtemps
- Servir correctement une SPA (Single Page Application) avec un fallback vers
index.html - Réduire la charge CPU/syscalls avec
sendfile,tcp_nopushetopen_file_cache
Dans quel contexte ?
Une équipe frontend déploie une application React buildée en fichiers statiques (app.a1b2c3.js, index.html) sur un serveur Nginx. Elle veut que les assets versionnés (avec un hash unique dans leur nom) soient mis en cache très longtemps par le navigateur, mais que chaque nouveau déploiement soit immédiatement visible pour tous les utilisateurs, sans qu'ils aient besoin de vider leur cache manuellement.
D'abord, comprendre try_files, la commande centrale de cette leçon
try_files $uri $uri/ =404; teste chaque chemin dans l'ordre donné : d'abord le fichier exact demandé, puis ce même chemin comme dossier (utile pour un index.html implicite), et enfin retourne une erreur 404 si rien n'a été trouvé. C'est un mécanisme de fallback en cascade, pas une simple vérification d'existence.
Une fois ce mécanisme compris, un cas particulier mérite un fallback différent
Une application React ou Vue gère son propre routage côté client (/produits/42 n'existe pas comme fichier réel sur le disque). try_files $uri $uri/ /index.html; renvoie systématiquement index.html pour toute route inconnue du système de fichiers, laissant le JavaScript de l'application décider quoi afficher — c'est la configuration standard pour toute SPA.
Ensuite, la question du cache se pose différemment selon le type de fichier
Un fichier avec un hash dans son nom (app.a1b2c3.js) ne changera jamais de contenu sous ce nom précis : un nouveau déploiement génère un nouveau hash, donc un nouveau nom de fichier. On peut donc lui appliquer un cache extrêmement long et agressif (Cache-Control: public, immutable), sans aucun risque.
| Type de contenu | Stratégie de cache | Raison |
|---|---|---|
Assets versionnés (app.a1b2c3.js) | expires 1y + immutable | Le nom change à chaque nouveau contenu |
| Images sans hash | expires 30d, sans immutable | Peuvent changer sans que le nom change |
| Fichiers HTML | expires -1 + no-cache, must-revalidate | Doit toujours refléter le dernier déploiement |
Il reste un piège classique à éviter absolument
Mettre en cache le HTML avec la même agressivité que les assets versionnés casse totalement le mécanisme de déploiement : les utilisateurs continueraient à charger une vieille page HTML référençant d'anciens fichiers JS supprimés, bien après un nouveau déploiement. Le HTML doit toujours être revalidé, jamais mis en cache longtemps.
Piège fréquent
Appliquer Cache-Control: public, immutable sur un fichier image dont le nom ne change jamais (comme logo.png) empêche toute mise à jour de cette image côté navigateur, même après un nouveau déploiement qui la remplace : les visiteurs verront l'ancienne image pendant toute la durée du cache. Réserve immutable aux fichiers réellement versionnés par leur nom.
Prérequis
Cette leçon suppose que tu es à l'aise avec les location et leurs priorités (leçon 3) : les règles de cache s'appliquent typiquement via des location basées sur l'extension du fichier.
Enfin, un dernier levier de performance, plus bas niveau
sendfile on; permet au noyau Linux de copier un fichier directement vers la socket réseau sans repasser par l'espace utilisateur, tcp_nopush regroupe les paquets avant envoi, et open_file_cache évite de rouvrir les mêmes descripteurs de fichiers à chaque requête. Ces réglages réduisent la charge CPU pour du contenu statique à fort volume.
Maintenant que le service de fichiers statiques est optimisé, la prochaine leçon aborde un sujet indissociable de toute mise en production sérieuse : sécuriser le trafic avec HTTPS et TLS.
Commandes & code
Fichiers statiques et cache
server {
listen 80;
server_name static.example.com;
root /var/www/static;
# try_files : essaie chaque chemin dans l'ordre, sert le premier qui existe
location / {
try_files $uri $uri/ =404;
}
# Cache long pour les assets versionnés (hash dans le nom : app.a1b2c3.js)
location ~* \.(js|css|woff2?|ttf|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Images : cache modéré, pas "immutable" (peuvent changer sans hash dans le nom)
location ~* \.(jpg|jpeg|png|gif|webp|ico)$ {
expires 30d;
add_header Cache-Control "public";
}
# HTML : jamais de cache long (sinon les déploiements ne se propagent pas)
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, must-revalidate";
}
}# Servir un SPA (React/Vue/Next export statique) : fallback vers index.html pour le routing côté client
server {
listen 80;
server_name app.example.com;
root /var/www/app/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html; # toute route inconnue retombe sur index.html
}
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off; # évite de polluer les logs pour chaque asset
}
}# sendfile / tcp_nopush / tcp_nodelay : optimisent l'envoi de fichiers statiques au niveau kernel
http {
sendfile on; # copie fichier -> socket directement dans le kernel (évite un aller-retour userspace)
tcp_nopush on; # regroupe les paquets avant envoi (efficace avec sendfile)
tcp_nodelay on; # désactive l'algorithme de Nagle pour les petites réponses (latence)
open_file_cache max=2000 inactive=20s; # cache les descripteurs de fichiers ouverts
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on; # cache aussi les "fichier non trouvé"
}# ETag et Last-Modified : validation de cache conditionnelle (304 Not Modified)
location /downloads/ {
etag on; # activé par défaut, génère un ETag basé sur mtime+taille
if_modified_since exact; # comparaison stricte de la date (défaut : "before", plus permissif)
}Résumé
try_filesteste des chemins dans l'ordre : indispensable pour les SPA (try_files $uri $uri/ /index.html;).- Assets versionnés (hash dans le nom) :
Cache-Control: public, immutable+expires 1ysans risque. - HTML jamais mis en cache longtemps : sinon les nouveaux déploiements restent invisibles pour les clients.
sendfile,tcp_nopush,open_file_cacheréduisent la charge CPU/syscalls pour le service de fichiers statiques.
Exercices pratiques
Mission : réparer une SPA qui affiche du contenu périmé après déploiement
Objectif : Configurer try_files pour le routage côté client d'une SPA et appliquer la bonne stratégie de cache par type de fichier.
Contexte
Une SPA React est déployée sur /var/www/app/dist. Actuellement, location / { try_files $uri $uri/ =404; } fait que toute route interne comme /produits/42 renvoie une 404 (aucun fichier réel n'existe à ce chemin). Par ailleurs, un développeur a appliqué Cache-Control: public, immutable avec expires 1y sur TOUT le dossier, y compris index.html, si bien que les visiteurs voient une version de l'application vieille de plusieurs déploiements.