Introduction
Nginx par défaut sert correctement quelques centaines de requêtes par seconde. Avec un tuning correct sur un VPS moderne, il dépasse facilement 10 000 req/s pour du contenu statique et 1000-3000 req/s pour du dynamique.
Ce tuto donne les paramètres essentiels pour :
- Maximiser les connexions simultanées
- Activer la compression (gzip + brotli)
- Optimiser le cache fichier statique
- Configurer TLS moderne (HTTP/2, OCSP stapling)
- Réduire la latence
Prérequis
- VPS Linux avec Nginx 1.18+
- Accès root
- Optimisations kernel déjà appliquées (voir
/docs/article/kernel-tuning)
Étape 1 : Identifier votre Nginx
nginx -V 2>&1
Notez les modules compilés (gzip, brotli, http2, etc.). Si brotli manque sur Debian/Ubuntu, installez libnginx-mod-brotli :
sudo apt install -y libnginx-mod-brotli
Étape 2 : Configurer les workers
Le worker est le process qui sert les requêtes. Sur un VPS multi-coeurs, configurez :
sudo nano /etc/nginx/nginx.conf
Bloc principal :
# Nombre de workers = nombre de CPUs
worker_processes auto;
# Augmenter les file descriptors par worker
worker_rlimit_nofile 65535;
events {
# Connexions simultanées par worker (worker_processes × worker_connections = max théorique)
worker_connections 16384;
# Multi-accept (accepte plusieurs connexions par event)
multi_accept on;
# epoll est le meilleur sur Linux
use epoll;
}
Avec worker_processes auto et 4 cores : 4 workers × 16384 = 65536 connexions max simultanées.
Étape 3 : Optimisations HTTP globales
Dans le bloc http { } :
http {
# === Performances de base ===
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# === Keepalive (réutilisation des connexions TCP) ===
keepalive_timeout 30;
keepalive_requests 1000;
# === Hash tables pour résoudre les serveurs plus vite ===
server_names_hash_bucket_size 128;
server_names_hash_max_size 4096;
types_hash_max_size 2048;
# === Tampons (buffers) ===
client_body_buffer_size 16K;
client_header_buffer_size 1k;
client_max_body_size 100M; # Uploads
large_client_header_buffers 4 16k;
# === Timeouts ===
client_body_timeout 12;
client_header_timeout 12;
send_timeout 10;
reset_timedout_connection on;
# === Hide version Nginx (sécurité) ===
server_tokens off;
}
Étape 4 : Activer la compression gzip
http {
# === Compression gzip ===
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/plain
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy
text/xml;
}
gzip_comp_level 6 est l'équilibre optimal (1 = vite/grosses tailles, 9 = compact/lent).
Étape 5 : Activer brotli (si installé)
Brotli compresse 15-25% mieux que gzip pour le texte. Supporté par tous les navigateurs modernes.
http {
# === Compression brotli ===
brotli on;
brotli_comp_level 6;
brotli_min_length 1024;
brotli_types
application/javascript
application/json
application/xml
application/atom+xml
application/rss+xml
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
}
Si vous voyez Content-Encoding: br dans les headers, brotli est actif.
Étape 6 : Configurer la mise en cache des fichiers statiques
http {
# === Cache fichier (file descriptors) ===
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
}
Et dans vos server { } blocks, ajoutez les headers de cache pour les statiques :
server {
# ...
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2|svg|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(pdf|zip|tar|gz)$ {
expires 7d;
add_header Cache-Control "public";
}
}
Étape 7 : Optimisations TLS / HTTPS
http {
# === SSL ===
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off; # En TLS 1.3, les clients choisissent
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
# === Session SSL ===
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 4h;
ssl_session_tickets off;
# === OCSP stapling (vérifie le cert sans round-trip client) ===
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=60s;
resolver_timeout 5s;
# === HSTS ===
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
Étape 8 : Activer HTTP/2 et HTTP/3
Dans chaque server { listen 443 ... } :
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
# HTTP/3 si Nginx 1.25+ avec module quic
# listen 443 quic reuseport;
# add_header Alt-Svc 'h3=":443"; ma=86400';
# ...
}
Vérifiez avec :
curl -I --http2 https://votre-domaine.com
Vous devez voir HTTP/2 200.
Étape 9 : Reverse proxy vers une app backend (PHP-FPM, Node, etc.)
Tuning spécifique pour réduire la latence :
upstream backend {
server 127.0.0.1:3000;
# Keepalive vers le backend (très important !)
keepalive 32;
keepalive_timeout 60s;
keepalive_requests 10000;
}
server {
location / {
proxy_pass http://backend;
# Forcer HTTP/1.1 pour le keepalive
proxy_http_version 1.1;
proxy_set_header Connection "";
# Headers à transmettre
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Buffer la réponse pour ne pas bloquer le backend
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 32 16k;
proxy_busy_buffers_size 32k;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
Étape 10 : Cache de proxy (très puissant pour les apps lentes)
http {
# === Cache proxy ===
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:100m max_size=10g inactive=60m use_temp_path=off;
}
server {
location / {
proxy_cache app_cache;
proxy_cache_revalidate on;
proxy_cache_min_uses 1;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
# Ajouter un header pour debug
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend;
# ... reste du config
}
}
Test :
curl -I https://votre-domaine.com
# Cherchez "X-Cache-Status: HIT" après le 2ème call
Étape 11 : Rate limiting (anti-flood)
http {
# Limite par IP
limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
}
server {
# Général : 30 req/s par IP, burst de 50
limit_req zone=general burst=50 nodelay;
location /login {
# Login : 5 req/min par IP (anti brute-force)
limit_req zone=login burst=2 nodelay;
# ...
}
}
Étape 12 : Tester la config et appliquer
sudo nginx -t
sudo systemctl reload nginx
Étape 13 : Benchmarker
# Installer wrk
sudo apt install -y wrk
# Test
wrk -t12 -c400 -d30s https://votre-domaine.com
Sortie type :
Running 30s test @ https://votre-domaine.com
12 threads and 400 connections
Thread Stats Avg Stdev Max
Latency 45ms 20ms 300ms
Req/Sec 1.5k 200 2k
Requests/sec: 17500
Transfer/sec: 30MB
Dépannage
"worker_connections exceeded"
Augmentez worker_connections ou worker_rlimit_nofile.
Erreur "Too many open files"
Le service ne respecte pas les limites système. Ajoutez à /etc/systemd/system/nginx.service.d/override.conf :
[Service]
LimitNOFILE=65535
sudo systemctl daemon-reload
sudo systemctl restart nginx
Latence élevée pour le PHP
Souvent dû à un PHP-FPM mal tuné. Augmentez pm.max_children dans le pool PHP-FPM :
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
Cache ne fonctionne pas
Vérifiez les permissions :
sudo chown -R www-data:www-data /var/cache/nginx
Et que vos requêtes ne contiennent pas de cookie (sinon le cache est skip par défaut).
Commandes utiles
# Tester la config
sudo nginx -t
# Recharger (sans coupure)
sudo systemctl reload nginx
# Voir les connexions actives
ss -tn | grep :443 | wc -l
# Top des IPs par requêtes (depuis access.log)
sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
# Status temps réel (nécessite module stub_status)
curl http://127.0.0.1/nginx_status
# Voir si gzip/brotli fonctionne
curl -I -H "Accept-Encoding: gzip, br" https://votre-domaine.com
# Profiling avec ngx_lua (si installé)
location /metrics {
content_by_lua_block { ngx.say("hello") }
}
Conclusion
Avec ces optimisations, votre Nginx est prêt pour la haute charge :
- HTTP/2 + keepalive + brotli → -40 à -60% bande passante client
- Cache proxy → réduit la charge du backend de 90%+
- Rate limiting → protection anti-flood applicative
Pour aller plus loin :
- Compilez Nginx avec ngx_brotli et ngx_lua depuis les sources
- Ajoutez Cloudflare devant pour offloader encore plus de cache
- Configurez WAF ModSecurity pour la sécurité applicative
Ressources
- Documentation Nginx : https://nginx.org/en/docs/
- Mozilla SSL Config Generator : https://ssl-config.mozilla.org


















