Intro
Nginx est largement déployé comme serveur web, proxy inverse et équilibreur de charge, car il est rapide et fiable dès sa configuration par défaut. Cependant, la configuration par défaut est volontairement prudente : elle fonctionne sur presque tout le matériel, mais laisse sur la table des performances significatives pour les charges à fort trafic ou sensibles à la latence. L'optimisation des performances ne consiste pas à augmenter aveuglément des limites ; c'est un processus systématique pour comprendre votre charge de travail, établir une référence quantifiable, effectuer une modification à la fois et vérifier le résultat. Sans cette discipline, vous risquez de déstabiliser un système en production ou de poursuivre des changements qui semblent bons isolément mais n'aident pas les utilisateurs réels.
Ce guide fournit une approche pratique, orientée commandes, destinée aux administrateurs Nginx et aux ingénieurs DevOps. Vous apprendrez à inventorier votre environnement, à modifier en toute sécurité les paramètres clés pour la diffusion de fichiers statiques et le proxying, à exécuter des tests de charge réalistes et à récupérer rapidement si un changement dégrade les performances. Chaque étape majeure inclut des commandes concrètes, les résultats attendus et des conseils d'interprétation.
Inventaire de la version et de l'environnement
Avant de toucher à la moindre directive, documentez exactement ce que vous exécutez. Le comportement de Nginx varie selon les versions et les compilations personnalisées, notamment avec les modules tiers, le support des threads et les options HTTP/2 ou SSL. Exécutez nginx -V pour voir la configuration de compilation. La sortie est longue, filtrez donc les indicateurs les plus pertinents :
nginx -V 2>&1 | tr ' ' '\n' | grep -E 'nginx/|--with-threads|--with-http_v2_module|--with-http_ssl_module'
La sortie ressemble à ceci :
nginx/1.24.0
--with-threads
--with-http_v2_module
--with-http_ssl_module
La présence de --with-threads signifie que vous pouvez utiliser des pools de threads pour les opérations bloquantes telles que les E/S disque. Sans cela, toutes les lectures disque s'effectuent dans la boucle d'événements du processus worker. Si vous voyez un chemin OpenSSL personnalisé ou des modules tiers comme --add-module=..., notez-les car ils peuvent affecter les performances TLS et l'utilisation de la mémoire.
Ensuite, enregistrez le noyau du système d'exploitation et les limites système. Les performances de Nginx sont étroitement liées aux limites de descripteurs de fichiers, aux tampons de socket et aux paramètres réseau :
ulimit -n
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog net.ipv4.ip_local_port_range
Une référence courante est :
ulimit -n
1024
Mais pour un serveur gérant des milliers de connexions simultanées, il vous faut au moins 65535. Nous y reviendrons plus tard.
Capturez également les informations matérielles et topologiques. Utilisez nproc pour obtenir le nombre de cœurs CPU, free -h pour la mémoire et lsblk ou iostat pour identifier le type de disque (HDD vs SSD). Par exemple :
nproc
free -h | head -2
lsblk -d -o NAME,ROTA,SIZE
Le champ ROTA indique si le disque est rotatif (1 pour HDD, 0 pour SSD). Cela compte car les performances de diffusion de fichiers statiques diffèrent considérablement entre HDD et SSD, surtout avec sendfile et les E/S directes.
Enfin, définissez le type de charge de travail. Nginx est-il :
- En train de servir des fichiers statiques directement depuis le disque ?
- En train de proxifier des requêtes vers un serveur d'application en amont (ex. Node.js, PHP-FPM, Java) ?
- En train de terminer des connexions TLS ?
- En train d'agir comme équilibreur de charge ?
Chaque scénario a un chemin d'optimisation différent. La diffusion de fichiers statiques bénéficie de sendfile, tcp_nopush et d'une mise en cache agressive. Le proxying bénéficie du keepalive en amont, du réglage des tampons et de la réutilisation des connexions HTTP/1.1. La terminaison TLS nécessite une mise en cache de session efficace et éventuellement une accélération matérielle.
Enregistrez toutes les constatations dans un simple fichier texte ou une page wiki. Vous vous référerez à cet inventaire pour décider quelles directives modifier et pour interpréter les résultats des benchmarks.
Chemin de configuration sûr
Modifier la configuration de production sans filet de sécurité est imprudent. Suivez ces règles :
- Sauvegardez tout d'abord. Copiez
/etc/nginx/nginx.confet tous les fichiers inclus (souvent dans/etc/nginx/conf.d/ou/etc/nginx/sites-enabled/) vers une sauvegarde horodatée. - Validez la syntaxe avant d'appliquer. Exécutez
nginx -t. Cela détecte les fautes de frappe, les points-virgules manquants et les directives invalides sans affecter le processus en cours. - Testez dans un environnement de préproduction ou canari. Si possible, exécutez une seconde instance Nginx sur un port non production (ex. 8080) avec la même configuration et une simulation de charge de travail.
- Utilisez un rechargement gracieux pour les changements en production.
nginx -s reloadlance de nouveaux processus worker avec la nouvelle configuration et arrête gracieusement les anciens workers après qu'ils aient terminé les requêtes existantes. Cela ne coupe pas les connexions actives. - Effectuez une modification à la fois. Changer cinq paramètres à la fois rend impossible de savoir lequel a causé l'amélioration ou la régression.
Passons maintenant à une configuration de référence concrète pour un serveur Nginx typique qui sert à la fois des fichiers statiques et proxifie les requêtes dynamiques. Commencez par le nginx.conf par défaut, puis appliquez les ajustements les plus généralement bénéfiques.
Processus worker et connexions
Les deux directives les plus influentes sont worker_processes et worker_connections.
worker_processes: doit être réglé surautosur Nginx moderne (1.3.8+). Cela crée un processus worker par cœur CPU, ce qui est optimal pour la plupart des charges de travail. Si vous avez de nombreuses poignées de main SSL gourmandes en CPU, vous pourriez le régler sur le nombre de cœurs moins un pour laisser de la marge au système d'exploitation, maisautoest un bon point de départ.worker_connections: définit le nombre maximal de connexions simultanées que chaque processus worker peut gérer. La valeur par défaut est souvent 512 ou 1024, ce qui est trop faible pour une forte concurrence. Le total des connexions =worker_processes * worker_connections. Pour un serveur qui doit gérer 10 000 clients simultanés, réglezworker_connectionsà 10240 ou plus, mais assurez-vous que la limite système (ulimit -n) est au moins le double (chaque connexion utilise un descripteur de fichier).
Voici un extrait de configuration de référence :
worker_processes auto;
events {
worker_connections 4096;
}
http {
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
upstream backend {
server 10.0.0.10:8080;
server 10.0.0.11:8080;
keepalive 32;
}
server {
listen 80;
server_name example.com;
location /static/ {
root /var/www;
expires 30d;
access_log off;
}
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
Détaillons chaque paramètre :
worker_processes auto;utilise tous les cœurs CPU.worker_connections 4096;permet à chaque worker de gérer 4096 connexions simultanées, pour un total théorique decœurs * 4096.sendfile on;active l'appel systèmesendfile(), qui copie les données directement du cache de pages du noyau vers le socket sans passer par l'espace utilisateur. Cela améliore considérablement le débit des fichiers statiques et réduit l'utilisation du CPU.tcp_nopush on;indique à Nginx d'envoyer les en-têtes de réponse HTTP dans un paquet et les données du fichier dans un autre, réduisant le nombre de paquets et améliorant l'efficacité du réseau. Cela fonctionne de concert avecsendfile.keepalive_timeout 65;maintient les connexions client ouvertes pendant 65 secondes d'inactivité, permettant aux requêtes suivantes de réutiliser la même connexion TCP. Cela réduit la surcharge des poignées de main TCP, surtout pour HTTPS.- Le bloc upstream définit un groupe backend avec deux serveurs et une directive
keepalive 32;. Cette directive ordonne à Nginx de maintenir jusqu'à 32 connexions keepalive inactives vers chaque serveur en amont. C'est crucial pour les performances du proxy inverse car cela évite la surcharge de poignée de main TCP et TLS à chaque requête. Sans cela, Nginx ouvre une nouvelle connexion vers le backend pour chaque requête. - Dans le bloc
location /,proxy_http_version 1.1;etproxy_set_header Connection "";sont requis pour activer le keepalive vers l'amont. Nginx utilise par défaut HTTP/1.0 pour le proxying, ce qui ferme la connexion après chaque requête sauf indication explicite. - L'emplacement statique définit
expires 30d;pour ajouter des en-têtes de mise en cache pour les navigateurs, etaccess_log off;pour réduire les E/S disque pour les requêtes de fichiers statiques.
Après avoir effectué ces modifications, validez et rechargez :
nginx -t
nginx -s reload
Sortie attendue :
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Si vous voyez une erreur, corrigez-la avant de continuer.
Ajustements au niveau du système
Parfois, la configuration Nginx est correcte, mais les limites du système d'exploitation l'empêchent d'utiliser sa pleine capacité. Pour une forte concurrence, augmentez la limite de fichiers ouverts pour l'utilisateur Nginx. Sur les systèmes basés sur systemd, créez un fichier de substitution :
mkdir -p /etc/systemd/system/nginx.service.d
printf '[Service]\nLimitNOFILE=65535\n' > /etc/systemd/system/nginx.service.d/limit.conf
systemctl daemon-reload
systemctl restart nginx
Vérifiez ensuite avec cat /proc/$(pgrep nginx | head -1)/limits | grep 'Max open files'. La valeur doit être 65535.
Réglez également les paramètres réseau du noyau pour gérer de nombreuses connexions. Ajoutez ce qui suit à /etc/sysctl.conf ou à un fichier dans /etc/sysctl.d/ :
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_fin_timeout = 15
net.core.netdev_max_backlog = 65535
Appliquez ensuite avec sysctl -p. Ces paramètres agrandissent la file d'acceptation, le backlog SYN et la plage de ports locaux, ce qui évite les abandons de connexions sous charge.
Vérification et diagnostics
Après avoir appliqué le réglage initial, vous avez besoin d'un benchmark reproductible pour mesurer l'impact. Ne vous fiez pas à ab pour des tests sérieux ; il est monothread et peut être optimiste. wrk et wrk2 sont des outils de benchmark HTTP modernes qui produisent des charges plus élevées et rapportent les percentiles de latence. Voici une commande de test réaliste pour un serveur Nginx servant un mélange de contenu statique et dynamique :
wrk -t4 -c100 -d30s --latency http://localhost/
Extrait de sortie attendue :
Running 30s test @ http://localhost/
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 23.45ms 12.34ms 210.50ms 78.9%
Req/Sec 2.13k 234.56 2.89k 68.0%
Latency Distribution
50% 18.23ms
75% 25.67ms
90% 35.89ms
99% 89.12ms
255456 requests in 30.02s, 320.11MB read
Requests/sec: 8508.45
Transfer/sec: 10.67MB
Analysez la sortie :
- Requêtes par seconde (RPS) : 8508,45. C'est la principale métrique de débit.
- Percentiles de latence : Le p99 de 89,12 ms signifie que 1 % des requêtes ont pris plus de 89 ms. Pour une API à faible latence, vous pourriez vouloir un p99 inférieur à 50 ms.
- Taux d'erreur :
wrkn'affiche pas les erreurs par défaut, mais si vous voyez des délais de connexion ou des réponses non-2xx, c'est un signal d'alarme.
Exécutez le même benchmark sur votre configuration de référence (avant les changements) et après chaque modification incrémentale. Gardez la durée du test et la concurrence identiques pour comparer.
Pour un test de latence plus contrôlé, utilisez wrk2 qui produit un taux de requêtes constant :
wrk2 -t4 -c100 -d30s -R2000 --latency http://localhost/
Cela envoie 2000 requêtes par seconde quelle que soit la capacité du serveur. Si le serveur ne peut pas suivre, la latence augmentera et vous pourrez identifier le point de rupture.
Surveillance interne de Nginx
Activez le module stub status pour voir les compteurs de connexions et de requêtes en direct. Ajoutez ceci à votre configuration :
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
Accédez-y avec curl http://localhost/nginx_status. La sortie ressemble à :
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
Interprétation :
- Connexions actives : total des connexions actuelles, y compris le keepalive inactif.
- accepts : total des connexions acceptées.
- handled : total des connexions traitées (égal à accepts si aucune limite de ressources n'est atteinte).
- requests : total des requêtes.
- Reading : connexions en train de lire les en-têtes de requête.
- Writing : connexions en train d'écrire des réponses.
- Waiting : connexions keepalive inactives attendant une autre requête.
Si handled est beaucoup plus bas que accepts, vous manquez de connexions worker. Si Waiting est élevé, le keepalive fonctionne bien.
Pour une analyse plus approfondie, vérifiez les ressources système pendant la charge :
top -b -n1 | head -20
vmstat 1 10
iostat -x 1 5
topmontre l'utilisation CPU des workers Nginx. Si chaque worker est à 100 % CPU, vous êtes limité par le CPU et pourriez avoir besoin de chemins de code plus efficaces ou de plus de cœurs.vmstatmontre la mémoire et les changements de contexte. Des changements de contexte élevés peuvent indiquer des appels système excessifs, peut-être dus à de nombreuses petites requêtes.iostatmontre l'utilisation du disque. Si%utilest proche de 100 % pour le disque servant les fichiers statiques, vous pourriez avoir besoin d'un SSD ou d'une meilleure mise en cache.
Modes de défaillance et récupération
Même un réglage minutieux peut introduire des problèmes. Voici les modes de défaillance courants et comment s'en remettre.
Épuisement des connexions worker
Symptôme : Nginx renvoie des erreurs 500 ou abandonne des connexions sous charge. nginx_status montre handled beaucoup plus bas que accepts. Le journal d'erreurs contient worker_connections are not enough.
Récupération : augmentez worker_connections et la limite de descripteurs de fichiers système (voir ci-dessus). Par exemple :
events {
worker_connections 8192;
}
Puis rechargez. Si le problème persiste, vérifiez dmesg pour nf_conntrack: table full ou augmentez net.core.somaxconn.
Problèmes de connexion en amont
Symptôme : les erreurs 502 Bad Gateway augmentent. Le journal d'erreurs indique upstream prematurely closed connection while reading response header.
Cause : souvent dû à des paramètres de tampon de proxy trop petits, ou à une configuration keepalive manquante qui fait que les serveurs en amont ferment les connexions inactives.
Correctif : assurez-vous d'avoir proxy_http_version 1.1; et proxy_set_header Connection ""; dans l'emplacement proxy. Définissez des tailles de tampon raisonnables :
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
Vérifiez aussi les délais d'attente du serveur en amont. Si le backend est lent, augmentez proxy_read_timeout (60 s par défaut est généralement suffisant).
Épuisement de la mémoire dû à de grands tampons
Symptôme : les processus worker Nginx sont tués par le tueur OOM. dmesg affiche Out of memory: Kill process. L'utilisation de la mémoire augmente de façon inattendue.
Cause : paramètres de tampon trop grands ou trop de réponses volumineuses mises en cache.
Correctif : réduisez les tailles de tampon ou définissez une limite sur la mise en mémoire tampon des réponses. Par exemple :
proxy_buffers 4 8k;
proxy_buffer_size 2k;
Ou désactivez la mise en mémoire tampon pour les grandes réponses en streaming (proxy_buffering off; pour des emplacements spécifiques).
Procédure de retour en arrière
Gardez toujours une sauvegarde de configuration connue comme bonne. Si un changement provoque une dégradation des performances ou des erreurs, restaurez la sauvegarde et rechargez :
cp /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf
nginx -t
nginx -s reload
Sortie attendue :
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Vérifiez ensuite avec curl -I http://localhost/ pour confirmer que le serveur répond normalement.
Si le serveur ne démarre pas après un changement, Nginx peut abandonner avec une erreur. Dans ce cas, l'ancien processus maître est toujours en cours d'exécution, mais vous pouvez également démarrer une instance séparée avec une configuration minimale pour rétablir le service :
nginx -c /etc/nginx/nginx.fallback.conf
Documentez chaque changement dans un fichier journal : ce que vous avez changé, pourquoi, et le benchmark avant et après. Cela permet d'identifier et de révoquer facilement les paramètres problématiques.
Liste de contrôle opérationnelle
Utilisez la liste de contrôle suivante pour chaque itération de réglage. Elle garantit un processus discipliné et reproductible.
| Étape | Action | Outil/Commande | Résultat attendu |
|---|---|---|---|
| 1 | Enregistrer les métriques de référence | wrk -t4 -c100 -d30s http://localhost/ | Enregistrer RPS et percentiles de latence |
| 2 | Identifier le goulot d'étranglement | top, vmstat, iostat, nginx_status | Déterminer si limité par CPU, E/S ou réseau |
| 3 | Modifier un paramètre | Éditer nginx.conf | Effectuer une seule modification documentée |
| 4 | Valider la configuration | nginx -t | Sortie : syntax is ok et test is successful |
| 5 | Appliquer le changement | nginx -s reload | Aucune erreur, nouveaux processus worker démarrés |
| 6 | Relancer le test de charge | Même commande wrk qu'à l'étape 1 | Métriques améliorées ou égales |
| 7 | Si amélioration, conserver | Documenter le changement | Noter dans le journal des modifications |
| 8 | Si régression, revenir en arrière | cp backup && nginx -s reload | Performances précédentes restaurées |
| 9 | Surveiller pendant 24 heures | nginx_status, journaux, métriques système | Aucune anomalie, performances stables |
Astuce : automatisez les benchmarks de référence et post-changement avec un script qui enregistre les résultats dans un fichier horodaté, afin de pouvoir facilement comparer entre les itérations.
Conclusion
L'optimisation des performances de Nginx n'est pas un événement ponctuel mais un processus itératif qui produit des améliorations mesurables lorsqu'il est abordé systématiquement. En inventoriant votre environnement, en effectuant des modifications ciblées, en vérifiant avec des métriques concrètes et en disposant d'un plan de retour en arrière, vous pouvez optimiser en toute sécurité pour votre charge de travail spécifique. Commencez par un projet pilote étroit et inspectable comme recommandé, puis étendez progressivement en fonction des résultats observés. Utilisez les commandes et la liste de contrôle fournies pour gagner en confiance et maintenir les performances au fil du temps. Avec la bonne méthodologie, vous pouvez libérer tout le potentiel de Nginx et garder vos applications rapides et fiables même sous les charges les plus lourdes.