Guide pratique pour accélérer un serveur web avec cache HTTP, compression Gzip/Brotli, réglages Nginx, tuning PHP-FPM et validation des gains en production.
Pourquoi optimiser avant de scaler
Quand un site devient lent, la première tentation est souvent d'ajouter plus de CPU, plus de RAM ou un serveur supplémentaire. C'est parfois nécessaire, mais pas en premier. Dans la plupart des cas, un mauvais cache HTTP, une compression absente, des buffers Nginx mal réglés ou un pool PHP-FPM trop agressif coûtent plus cher que l'infrastructure elle-même.
L'optimisation serveur a un objectif simple : servir plus vite avec moins de ressources. Cela améliore le temps de réponse utilisateur, réduit la charge backend, limite les pics de consommation et donne plus de marge en production. Un site correctement réglé supporte mieux les pointes de trafic et reste plus stable pendant les déploiements.
Comprendre les couches de cache
Le mot cache recouvre plusieurs réalités. Pour éviter les erreurs de conception, il faut distinguer les couches suivantes :
| Couche | Rôle | Exemple concret |
|---|---|---|
| Cache navigateur | Évite de retélécharger les assets | CSS, JS, images avec Cache-Control |
| Cache reverse proxy | Réutilise une réponse déjà calculée | Nginx FastCGI cache pour une page publique |
| Cache applicatif | Mémorise un résultat métier | Redis pour une liste de produits ou un menu |
| Cache PHP interne | Évite de recompiler le code | OPCache pour les scripts PHP |
Un bon tuning combine généralement ces quatre niveaux. Le navigateur gère les fichiers statiques, Nginx accélère les réponses répétitives, l'application cache les calculs coûteux et PHP évite de reparser le code à chaque requête.
Configurer le cache HTTP pour les assets statiques
Le premier levier rentable concerne les fichiers statiques : CSS, JavaScript, polices, SVG, images et bundles de build. Quand les noms de fichiers sont versionnés ou hashés, vous pouvez pousser un cache long sans risque majeur.
server {
listen 80;
server_name example.com;
root /var/www/example.com;
location ~* \.(css|js|jpg|jpeg|png|gif|svg|webp|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
access_log off;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
Cette configuration diminue immédiatement la bande passante et le nombre de requêtes servies par votre application. Pour un frontend construit avec Angular, React ou Vite, c'est généralement le réglage de base à appliquer dès la première mise en production.
Exemple concret : un utilisateur recharge la page cinq fois. Sans cache, le navigateur redemande à chaque fois les bundles JS et le CSS principal. Avec un header long et immutable, il réutilise ses fichiers locaux tant que le nom du fichier ne change pas.
app.css, préférez un TTL plus court ou ajoutez un paramètre de version au build.
Activer la compression Gzip et Brotli
Une page HTML, une réponse JSON ou un bundle JavaScript compressé voyagent beaucoup plus vite sur le réseau. La compression ne remplace pas le cache, mais les deux se complètent très bien.
Configuration Gzip recommandée dans Nginx :
http {
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_proxied any;
gzip_types
text/plain
text/css
text/xml
application/json
application/javascript
application/xml+rss
image/svg+xml;
}
Le niveau gzip_comp_level 5 offre souvent un bon compromis entre coût CPU et réduction de taille. Monter plus haut apporte parfois peu de gain supplémentaire tout en consommant davantage de ressources sur des serveurs déjà sollicités.
Si Brotli est disponible :
http {
brotli on;
brotli_comp_level 5;
brotli_static on;
brotli_types
text/plain
text/css
application/json
application/javascript
image/svg+xml;
}
Brotli compresse généralement mieux que Gzip pour les assets texte. Sur un site moderne, cela réduit surtout le poids des bundles JS et du CSS principal.
Mettre en cache les réponses dynamiques utiles
Toutes les pages dynamiques ne doivent pas être recalculées à chaque requête. Une page d'accueil publique, une page catégorie, un flux JSON public ou une page article changent rarement à la seconde. Elles sont donc de bonnes candidates pour un cache proxy court.
Exemple de FastCGI cache avec Nginx :
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=PHPZONE:100m inactive=30m max_size=2g;
server {
listen 80;
server_name example.com;
root /var/www/example.com/public;
set $skip_cache 0;
if ($request_method = POST) {
set $skip_cache 1;
}
if ($query_string != "") {
set $skip_cache 1;
}
if ($http_cookie ~* "PHPSESSID|wordpress_logged_in|comment_author") {
set $skip_cache 1;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_cache PHPZONE;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_valid 200 10m;
fastcgi_cache_valid 404 1m;
add_header X-FastCGI-Cache $upstream_cache_status;
}
}
Dans cet exemple, les requêtes POST, les URLs avec query string et les utilisateurs connectés ne sont pas mis en cache. C'est une règle simple mais efficace pour les sites de contenu et les pages publiques.
Exemple concret : une page article consultée 10 000 fois dans la journée est calculée une seule fois toutes les 10 minutes au lieu d'exécuter PHP 10 000 fois. Le gain CPU et la baisse du temps de réponse sont souvent visibles immédiatement.
Tuner PHP-FPM et OPCache
Une fois le cache HTTP en place, il faut s'assurer que les requêtes qui atteignent réellement PHP sont traitées efficacement. Le tuning de PHP-FPM dépend de votre RAM disponible, du poids moyen de vos scripts et du niveau de concurrence attendu.
Exemple de pool raisonnable pour une VM moyenne :
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500
request_terminate_timeout = 60s
request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm.slow.log
Le piège classique consiste à mettre pm.max_children trop haut. Si chaque worker consomme 80 Mo en charge réelle, vingt workers représentent déjà environ 1,6 Go de RAM, sans compter Nginx, MariaDB, Redis et le système. Un chiffre trop ambitieux provoque swap, latence et instabilité.
Réglages OPCache recommandés :
[opcache]
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.save_comments=1
realpath_cache_size=4096K
realpath_cache_ttl=600
En production, opcache.validate_timestamps=0 est efficace à condition d'avoir un vrai processus de déploiement qui redémarre ou recharge PHP-FPM après publication. Sans cela, vous risquez de servir un ancien code alors que les fichiers ont changé sur disque.
Mesurer les gains et éviter les faux positifs
Une optimisation non mesurée reste une hypothèse. Il faut valider le résultat avec des commandes simples, reproductibles et interprétables.
Vérifier les headers HTTP :
curl -I https://example.com/assets/app.4fd2.js
curl -I -H "Accept-Encoding: gzip, br" https://example.com/
curl -I https://example.com/article-exemple
Vous devez contrôler au minimum la présence de Cache-Control, Content-Encoding, Vary: Accept-Encoding et, si vous utilisez un cache proxy, un header de debug comme X-FastCGI-Cache.
Faire un test de charge simple :
# 500 requêtes avec 20 connexions concurrentes
ab -n 500 -c 20 https://example.com/
# Ou avec wrk si disponible
wrk -t4 -c50 -d30s https://example.com/
Comparez avant/après sur trois indicateurs : temps moyen, débit de requêtes par seconde et consommation CPU. Si le temps baisse mais que le CPU explose, votre compression ou votre stratégie de cache mérite peut-être d'être ajustée.
Checklist de mise en production
- Assets statiques : TTL long uniquement si les noms de fichiers sont versionnés ou hashés.
- Compression : activez Gzip par défaut, Brotli si votre stack le permet sans complexité excessive.
- Cache dynamique : commencez sur les pages publiques et excluez clairement session, admin, POST et query strings sensibles.
- PHP-FPM : calculez
pm.max_childrenà partir de la RAM réelle, pas au hasard. - OPCache : activez-le systématiquement en production avec une vraie procédure de reload après déploiement.
- Logs : surveillez les slow logs PHP-FPM, les erreurs Nginx et la taille du cache disque.
- Mesure : conservez un jeu minimal de commandes
curl,abouwrkpour valider vos changements. - Progressivité : déployez d'abord les optimisations les plus sûres, puis ajoutez le cache dynamique par petites étapes.
Conclusion
L'optimisation des performances serveur repose rarement sur une seule "astuce magique". Les meilleurs résultats viennent d'un ensemble cohérent de réglages : cache navigateur pour les assets, compression HTTP pour les contenus texte, cache proxy sur les pages publiques, puis tuning mesuré de PHP-FPM et d'OPCache.
Si vous ne devez appliquer que trois actions aujourd'hui, commencez par ajouter un vrai Cache-Control sur les fichiers statiques, activer Gzip ou Brotli et vérifier que PHP utilise OPCache correctement. Ensuite seulement, avancez vers le cache dynamique et les réglages plus fins de concurrence.
Une stack bien réglée coûte moins cher, répond plus vite et supporte mieux les pics de trafic. En production, la performance durable n'est pas un sprint d'optimisation ponctuel, mais une discipline de mesure, d'ajustement et de validation continue.