Mise en cache Apache : accélérer un site sans casser les mises à jour
Il y a un paradoxe amusant dans le métier : on passe des heures à optimiser une requête SQL, puis on laisse le serveur renvoyer vingt fois la même image de 400 Ko à chaque visite. La mise en cache, c'est un peu l'écologie du web. On économise de la bande passante, on réduit la charge serveur, on accélère la page. Et pourtant, c'est souvent la dernière chose qu'on configure, quand on y pense. En 2026, avec les Core Web Vitals qui pèsent réellement sur le référencement et des utilisateurs de plus en plus exigeants sur la vitesse, la mise en cache n'est plus un bonus. C'est une nécessité de base. Mais attention : un cache mal configuré peut aussi bloquer une mise à jour pendant des semaines, ce qui est bien plus grave qu'un site un peu lent.
Je vais vous expliquer comment configurer la mise en cache sur Apache sans tomber dans les pièges classiques. On va voir les en-têtes HTTP, le rôle de mod_expires, la compression avec mod_deflate, le cache serveur avec mod_cache, et les stratégies de déploiement pour ne jamais livrer un utilisateur bloqué sur une vieille version. Prenez un café, on va faire le tour de la question, sans jargon inutile.
Pourquoi la mise en cache est le levier de performance le plus rentable
La mise en cache n'est pas un truc de geek pour gagner trois millisecondes. C'est un changement d'ordre de grandeur. Un fichier servi depuis le cache local du navigateur ne génère aucune requête, aucun transfert, aucun traitement serveur. Sur une visite repeat, c'est 60 à 80 % des ressources qui peuvent être servies sans passer par le réseau. Ça change tout. Et pour le serveur, c'est autant de processus libres pour les visiteurs qui arrivent vraiment.
Ce que gagne un site quand il cache bien
Les gains se mesurent sur plusieurs axes. D'abord le temps de chargement perçu, qui s'améliore nettement dès que les images, les CSS et les polices sont en cache. Ensuite la charge serveur : moins de requêtes par visite, donc moins de CPU, moins de bande passante, et une facture d'hébergement qui reste stable même en cas de pic de trafic. Enfin, l'expérience utilisateur, qui se traduit par un meilleur taux de rebond et, pour un site marchand, par un panier moyen plus élevé. On ne cache pas pour le plaisir, on cache pour vendre plus et pour que les gens reviennent.
Les trois niveaux de cache à ne pas confondre
Il faut distinguer trois niveaux, qui travaillent ensemble mais qui se configurent séparément :
- Le cache navigateur, piloté par les en-têtes HTTP : le navigateur stocke les fichiers et les réutilise sans requête
- Le cache intermédiaire, géré par un CDN ou un proxy : c'est lui qui sert les assets depuis un point proche de l'utilisateur
- Le cache serveur, géré par
mod_cache: Apache stocke sur disque ou en mémoire les réponses pour les resservir plus vite
Ces trois niveaux ne font pas le même travail, et les confondre conduit à des configurations incohérentes. Par exemple, un cache serveur qui conserve une page dynamique pendant une heure, c'est une catastrophe pour un site e-commerce. Alors que le même cache sur des images statiques, c'est du pur bonheur. Chaque niveau doit avoir ses règles, explicites et documentées.
Les en-têtes HTTP : la base de tout
Sans en-têtes, votre cache ne fonctionne pas. Ce sont eux qui disent au navigateur et aux proxys ce qu'ils peuvent faire, pendant combien de temps, et à quelles conditions. Les directives d'Apache ne font que produire ces en-têtes. Comprendre les en-têtes, c'est comprendre le cache.
Cache-Control, Expires, ETag, Last-Modified : qui fait quoi
| En-tête | Rôle | Priorité |
|---|---|---|
Cache-Control |
Indique la politique de cache, avec des directives comme max-age, no-cache, no-store, public ou private |
Prioritaire sur Expires |
Expires |
Date absolue d'expiration de la ressource, format HTTP | Utilisé si Cache-Control est absent |
ETag |
Empreinte de la ressource, permet une validation conditionnelle | Complémentaire |
Last-Modified |
Date de dernière modification, validation conditionnelle | Complémentaire |
En pratique, on privilégie Cache-Control qui est plus précis et mieux supporté. Expires reste utile pour les vieux clients, mais c'est de moins en moins nécessaire. ETag et Last-Modified permettent un cache conditionnel : si la ressource n'a pas changé, le serveur renvoie un 304 Not Modified, ce qui économise la bande passante. C'est un mécanisme élégant et presque gratuit, que tout serveur devrait activer.
max-age, s-maxage, must-revalidate, immutable
La directive max-age indique en secondes la durée de fraîcheur de la ressource. Une ressource fraîche est servie sans requête. s-maxage ne concerne que les caches partagés, comme un CDN : c'est utile quand on veut une durée différente pour les proxys et pour les navigateurs. must-revalidate impose une vérification une fois la ressource expirée, ce qui évite de servir du contenu obsolète. immutable, introduit plus récemment, indique que la ressource ne changera jamais à cette URL, ce qui autorise le navigateur à ne plus la revérifier du tout, même après un rechargement. Très utile pour des assets versionnés.
Ce que les navigateurs font réellement de ces en-têtes
Les navigateurs respectent globalement les en-têtes, mais avec des nuances. Un rechargement forcé (Ctrl+F5) demande souvent une revalidation complète, même sur des ressources immutable. Certains navigateurs changent de comportement en navigation privée. Et un utilisateur qui vide son cache perd tout. Ces comportements ne se contrôlent pas, il faut donc les considérer comme des constantes. La bonne approche consiste à faire confiance aux en-têtes pour le cas nominal, et à fournir un mécanisme de cache-busting pour les cas exceptionnels.
mod_expires : poser une politique de fraîcheur
mod_expires est le module le plus simple pour configurer le cache. Il génère automatiquement les en-têtes Expires et Cache-Control en fonction du type de fichier ou d'une durée relative. C'est rapide à mettre en place et suffisant pour la grande majorité des sites.
Activer le module et vérifier qu'il est chargé
Sur Debian et Ubuntu, la commande a2enmod expires active le module. Sur RHEL, il faut s'assurer que le LoadModule correspondant est présent. La vérification se fait avec apachectl -M | grep expires, qui doit afficher le module chargé. Si vous ne voyez rien, inutile de continuer : le module n'est pas lu et toutes les directives ExpiresByType seront ignorées, souvent sans message clair. C'est la première cause d'échec d'une politique de cache.
ExpiresByType expliqué sans jargon
La directive ExpiresByType associe une durée de vie à un type MIME. La syntaxe peut paraître obscure au premier abord, avec des valeurs comme access plus 1 month ou modification plus 1 week. En réalité, c'est simple : access signifie « à partir de la requête », et modification signifie « à partir de la dernière modification du fichier ». Dans la pratique, on utilise presque toujours access, qui donne un comportement prévisible. La durée s'exprime en secondes, minutes, heures, jours, semaines, mois ou années. Une syntaxe comme 1 year est parfaitement valide.
Une politique par famille de fichiers : images, CSS, JS, HTML
La règle de base consiste à distinguer les fichiers statiques, qui changent rarement, du HTML, qui change souvent. Une politique éprouvée ressemble à ceci :
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType text/html "access plus 0 seconds"
</IfModule>
Les images et les polices ont un cache long, car elles ne changent presque jamais à URL constante. Les CSS et JS ont un cache moyen, typiquement un mois, avec hash de version dans le nom de fichier. Le HTML n'a pas de cache, ce qui garantit que les mises à jour de contenu sont immédiatement visibles. C'est un compromis classique, qui marche pour la plupart des sites. On peut l'affiner en fonction des contraintes de chaque projet.
mod_headers : reprendre le contrôle manuellement
mod_expires ne couvre pas tous les cas. Pour des règles plus fines, on passe par mod_headers, qui permet d'ajouter ou de retirer n'importe quel en-tête, sur n'importe quel chemin.
Ajouter Cache-Control sur des chemins précis
On peut écrire un bloc <FilesMatch> qui cible un type de fichier et applique un en-tête personnalisé. Par exemple, pour forcer un cache long sur les assets situés dans un sous-répertoire versionné :
<FilesMatch "\.(jpg|png|webp|woff2)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
Cette approche est plus flexible que mod_expires, mais aussi plus verbeuse. Elle permet d'ajouter des directives comme stale-while-revalidate, qui autorise le navigateur à servir une version ancienne pendant qu'il actualise en arrière-plan. C'est une technique moderne, très utile pour les sites à fort trafic.
Header unset et les en-têtes à retirer
Parfois, il faut retirer un en-tête, et pas seulement en ajouter. Les en-têtes Server et X-Powered-By peuvent révéler des informations sensibles sur votre infrastructure. Header unset Server et Header unset X-Powered-By les suppriment, à condition que le module soit bien configuré. Cela ne remplace pas une politique de sécurité complète, mais cela réduit la surface d'information disponible pour un attaquant. En 2026, c'est un minimum.
Le piège : forcer un cache long sur du contenu dynamique
Il faut résister à la tentation de forcer un cache long sur du HTML ou sur des réponses d'API. Une page dynamique changeante doit rester fraîche, ou avoir un mécanisme d'invalidation. Un cache long sur du HTML peut afficher une promotion expirée, un prix périmé, ou pire, une erreur pendant des heures. Sur un e-commerce, c'est catastrophique. Le cache long, c'est pour les assets immuables, pas pour le contenu qui vit.
mod_deflate et mod_brotli : compresser avant de cacher
La compression n'est pas strictement du cache, mais elle va de pair. Transférer 100 Ko au lieu de 300 Ko, c'est autant de gagné pour l'utilisateur et pour le serveur. Le cache et la compression sont deux leviers complémentaires.
Configurer mod_deflate proprement
mod_deflate compresse les réponses à la volée, en gzip. Il faut activer le filtre sur les types MIME textuels, pas sur les images déjà compressées. Voici une configuration courante :
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml
AddOutputFilterByType DEFLATE text/css application/javascript
AddOutputFilterByType DEFLATE application/json application/xml
</IfModule>
Cette configuration compresse le HTML, le CSS, le JS et les données structurées. Elle exclut les images, les vidéos et les archives, qui sont déjà compressées. Compresser un PNG ou un ZIP est une perte de CPU, sans gain réel. C'est un piège fréquent, surtout quand on active DEFLATE sur tous les types sans réfléchir.
Brotli en 2026 : est-ce réellement utile ?
Brotli (mod_brotli) offre un meilleur taux de compression que gzip sur les fichiers textuels, avec un coût CPU légèrement supérieur. En 2026, il est bien supporté par les navigateurs modernes. Sur des sites statiques, le gain peut être de 15 à 20 % par rapport à gzip. Sur des sites dynamiques, la compression à la volée peut consommer du CPU, mais c'est souvent acceptable. Le compromis dépend du trafic et de la puissance du serveur. Si vous avez le temps, activez Brotli en plus de gzip, avec une priorité côté client. Une configuration type utilise mod_brotli avec les mêmes types de fichiers que gzip.
Ce qu'il ne faut jamais compresser
Les fichiers déjà compressés, comme les JPG, PNG, WEBP, ZIP, GZ, MP4 et PDF, ne doivent pas être recompressés. De même, les flux chiffrés et les données binaires sensibles ne doivent pas être touchés. Enfin, ne compressez pas les réponses très petites, car l'en-tête gzip peut être plus volumineux que le contenu. Ces règles de bon sens évitent des configurations contre-productives.
mod_cache : le cache serveur proprement dit
mod_cache permet à Apache de stocker des réponses et de les resservir, ce qui est utile pour du contenu statique ou des réponses d'API. Mais c'est aussi le module le plus piégeux, parce qu'un cache serveur mal configuré peut servir du contenu périmé à tous les utilisateurs, sans moyen simple de le corriger.
mod_cache_disk et mod_cache_socache : lequel choisir
mod_cache_disk stocke les réponses sur disque, ce qui est adapté aux gros volumes. mod_cache_socache utilise un cache partagé en mémoire, plus rapide mais de capacité limitée. Sur un site à fort trafic avec des assets volumineux, le cache disque est souvent préféré. Sur une API à faible latence, le cache mémoire peut suffire. Le choix dépend du volume et du profil d'accès.
CacheEnable, CacheRoot et les chemins de stockage
La directive CacheEnable active le cache pour une URL et un type de stockage. CacheRoot définit le répertoire de stockage, qui doit être accessible en écriture par l'utilisateur Apache. La configuration de base ressemble à ceci :
CacheEnable disk /assets
CacheRoot /var/cache/apache2/mod_cache_disk
CacheDirLevels 2
CacheDirLength 1
Ces directives limitent le cache au répertoire /assets, ce qui est prudent. Les répertoires de cache doivent être dimensionnés, surveillés, et nettoyés régulièrement. Un cache disque mal dimensionné peut remplir une partition, ce qui provoque des erreurs 500 difficiles à anticiper. En production, il faut placer le cache sur une partition dédiée quand c'est possible.
CacheDefaultExpire et CacheMaxExpire
CacheDefaultExpire définit la durée par défaut quand la réponse ne contient pas d'en-tête de cache. CacheMaxExpire fixe une limite supérieure, ce qui évite qu'une réponse avec un max-age énorme ne reste en cache indéfiniment. Ces deux directives sont vos garde-fous. Sans elles, vous dépendez entièrement de la configuration de l'application, qui n'est pas toujours fiable.
Le cache est-il fait pour votre site ? Les cas où il dessert
Le cache serveur n'est pas une solution universelle. Sur un site avec beaucoup de contenu personnalisé, il est complexe à configurer et peut créer plus de problèmes qu'il n'en résout. Sur un site où les utilisateurs sont authentifiés, il faut désactiver le cache sur les requêtes concernées, ce qui est loin d'être trivial. Sur un site avec des mises à jour fréquentes, l'invalidation devient un casse-tête. Le cache serveur est vraiment utile pour des assets statiques et des réponses d'API publiques et stables. Pour tout le reste, un bon cache navigateur et un CDN font souvent mieux.
Valider le cache sans casser les déploiements
Le vrai défi du cache n'est pas de l'activer, c'est de le mettre à jour. Un cache cassé, c'est un utilisateur bloqué sur une vieille version, avec une expérience dégradée et parfois un panier cassé. La stratégie de déploiement est aussi importante que la politique de cache.
Le cache-busting par nom de fichier : la méthode la plus fiable
La méthode la plus fiable consiste à versionner les fichiers dans leur nom. Quand vous modifiez style.css, vous le renommez style.a1b2c3.css. L'URL change, donc le navigateur recharge. L'ancienne version reste en cache, mais elle n'est plus utilisée. Cette approche est simple, robuste, et compatible avec n'importe quel cache. C'est ce que font tous les outils modernes, de Vite à Webpack en passant par Rails.
Le rôle des versions d'assets dans un pipeline de build
Dans un pipeline moderne, le build produit des fichiers avec un hash de contenu, et génère une page HTML qui référence ces noms. Le cache navigateur peut être long sur les assets, parce que le nom change à chaque modification. Le HTML, lui, reste sans cache, ou avec un cache très court. C'est la combinaison qui rend le déploiement invisible pour l'utilisateur, tout en gardant les avantages du cache long. Cette architecture demande un peu d'organisation, mais elle est maintenant standard.
Purger le cache : CacheDisable, fichiers, redémarrages
En cas d'urgence, on peut désactiver le cache avec CacheDisable, nettoyer le répertoire de stockage, ou redémarrer Apache. Un redémarrage n'invalide pas forcément le cache disque, il faut donc vérifier les fichiers stockés. Sur un CDN, la purge se fait par API. Prenez l'habitude de documenter la procédure de purge, parce qu'en situation de crise, on n'a pas envie de chercher dans la documentation. Une note interne, même brève, fait gagner de précieuses minutes.
Mesurer l'impact réel
Activer le cache ne suffit pas. Il faut vérifier que ça fonctionne, et mesurer le gain. Plusieurs méthodes simples existent, et les associer permet d'avoir une vision complète.
Les en-têtes vus avec curl -I
curl -I https://example.com/style.css affiche les en-têtes de réponse, dont Cache-Control, Expires et ETag. C'est le moyen le plus direct de vérifier la politique. Attention : si vous testez plusieurs fois, curl ne simule pas un cache local. Pour vérifier un comportement réel, utilisez l'option -H "If-None-Match: ..." et observez si le serveur renvoie un 304.
Les DevTools et le panneau Réseau
Dans Chrome ou Firefox, le panneau Réseau indique pour chaque requête si elle a été servie depuis le cache, avec la colonne « Size (from cache) » ou similaire. Rechargez la page une deuxième fois, vous verrez les assets servir depuis le cache. Inspectez les en-têtes dans l'onglet « Headers », vous verrez la configuration réelle. C'est un outil indispensable pour valider une configuration.
Les indicateurs Core Web Vitals
Le cache améliore directement le LCP et le FCP, car les ressources se chargent plus vite. Utilisez PageSpeed Insights et les données de terrain pour mesurer l'impact. Attention toutefois : le cache n'améliore pas les métriques pour un nouvel utilisateur, il les améliore pour les visites repeat. Pour les nouveaux utilisateurs, ce sont le CDN et le poids des ressources qui comptent.
Les erreurs classiques de mise en cache
Les erreurs de cache sont souvent silencieuses, ce qui les rend d'autant plus dangereuses. Elles n'apparaissent pas dans les logs, et les utilisateurs se plaignent sans qu'on puisse identifier la cause. Voici celles que je rencontre le plus souvent.
Un cache trop long sur des pages HTML dynamiques
C'est l'erreur classique. Un cache d'une journée sur des pages HTML produit des utilisateurs bloqués sur une ancienne version, avec des promotions expirées ou des articles obsolètes. La correction consiste à réduire la durée, ou à mettre en place un mécanisme d'invalidation. Pour du HTML, je conseille un cache de l'ordre de quelques minutes, ou un cache avec revalidation systématique.
Assets non versionnés et utilisateurs bloqués sur une vieille version
Si vous mettez à jour app.js sans changer son nom, les utilisateurs avec un cache long continuent d'utiliser l'ancienne version. Les bugs corrigés restent visibles, et le support croule sous les plaintes. La solution est simple : versionnez les noms de fichiers. C'est une des raisons pour lesquelles les outils de build modernes génèrent des hash dans les noms.
La compression activée sur des fichiers déjà compressés
Une configuration trop large de mod_deflate compresse les images, les archives ou les vidéos. Le serveur dépense du CPU sans gain. Pire, sur certains fichiers binaires, la compression peut amplifier légèrement la taille. La correction consiste à restreindre explicitement les types MIME compressés aux seuls fichiers textuels.
Un cache serveur qui ne s'invalide jamais
Un cache disque configuré sans mécanisme d'invalidation peut servir des réponses obsolètes pendant des mois. Les utilisateurs voient des données périmées, et l'administrateur ne comprend pas. Il faut soit un mécanisme de purge automatique, soit une durée de vie courte sur les contenus volatils. Un cache serveur doit être surveillé et documenté, comme n'importe quelle infrastructure.
FAQ : les questions les plus fréquentes sur le cache Apache
Faut-il cacher le HTML ? Avec prudence. Un cache de quelques minutes est acceptable pour des pages publiques stables. Pour des pages personnalisées, il faut un cache au niveau de l'application, ou désactiver le cache sur ces URL. Le HTML est le contenu le plus sensible, il faut donc l'aborder avec précaution.
Quelle durée pour les images et les polices ? Un an est la norme, avec versionnement dans le nom de fichier. Les images et les polices ne changent jamais sans changer de nom, ce qui permet un cache long sans risque. C'est le cas d'usage idéal pour immutable.
Comment vérifier qu'un fichier est bien servi depuis le cache ? Utilisez les DevTools du navigateur, panneau Réseau. Rechargez la page deux fois, et observez si les assets apparaissent avec la mention « from cache ». Vérifiez les en-têtes pour confirmer la politique. C'est simple et rapide.
Conclusion : une politique de cache écrite, testée et documentée
La mise en cache n'est pas un réglage qu'on active puis qu'on oublie. C'est une politique, avec des règles claires, des durées décidées, et des procédures de mise à jour. Une bonne politique de cache accélère le site, réduit la charge serveur et améliore l'expérience utilisateur. Une mauvaise politique bloque les mises à jour, casse les parcours d'achat et finit par coûter plus cher qu'elle ne rapporte. En 2026, avec des sites de plus en plus riches et des utilisateurs de plus en plus impatients, il n'y a plus de place pour le compromis. Écrivez votre politique, testez-la, documentez-la, et ajustez-la au fil des déploiements. C'est un peu de rigueur au départ, mais c'est ce qui distingue un site rapide d'un site qui rame. Et vos utilisateurs, même s'ils ne le savent pas, vous en seront reconnaissants.