Performance et scalabilité Nginx

...

Optimiser les worker_processes et worker_connections

Le réglage de worker_processes et worker_connections est le premier levier que tu dois actionner pour scaler Nginx. Je vois trop de configs avec un seul worker_process sur des machines multi-cœurs, c'est un gâchis. Nginx est conçu pour gérer des milliers de connexions simultanées, mais encore faut-il lui donner les moyens de le faire.

worker_processes définit le nombre de processus workers. Chaque worker est indépendant et peut traiter des requêtes en parallèle. La règle de base : égal au nombre de cœurs CPU disponibles. Si tu as un doute, utilise auto. Nginx détectera lui-même le nombre de cœurs. Sur un serveur avec 4 cœurs, ça donnera 4 workers. Attention : ne mets pas plus que le nombre de cœurs, sinon tu risques de surcharger le CPU avec du context switching inutile.

worker_connections fixe le nombre maximum de connexions qu'un worker peut gérer simultanément. Le nombre maximum de clients simultanés est donc worker_processes * worker_connections. Par défaut, c'est 512, mais sur un serveur moderne tu peux monter à 1024, 2048 ou plus, en fonction de la RAM disponible. Chaque connexion consomme un peu de mémoire, donc surveille l'utilisation avec htop ou ps aux.

Voici un exemple de configuration dans /etc/nginx/nginx.conf qui correspond à ce que j'utilise sur mes serveurs de production :

auto pour les processus, et 1024 connexions par worker. Avec 4 cœurs, ça te donne 4096 connexions simultanées avant d'atteindre la limite. Attention aussi à la limite système d'ulimit : vérifie que ulimit -n est suffisamment haut (ex: 65535). Sinon Nginx ne pourra pas ouvrir assez de fichiers.

Mon conseil : commence avec ces valeurs et monitor les logs d'erreur. Si tu vois des messages "worker_connections are not enough", augmente progressivement. Mais n'oublie pas que la performance dépend aussi des autres directives que nous allons voir ensuite (compression, cache, etc.). Bien régler ces deux paramètres te donne une base solide pour scaler.

Activer la compression gzip pour réduire la bande passante

La compression gzip est le levier le plus immédiat pour alléger tes transferts HTTP. Trop de sites l'ignorent alors qu'une simple directive peut diviser la bande passante par trois. Sur mes serveurs, j'ai vu le volume de données passer de 500 ko à 150 ko pour une même page. Ça change tout quand tu scales sous forte charge.

Active gzip dans /etc/nginx/nginx.conf ou ton fichier de http {}. Voici une config que j'utilise et qui fonctionne du premier coup :

Quelques explications. gzip_types exclut les formats déjà compressés : les images JPEG, PNG, GIF, les archives ZIP, etc. Les compresser gaspillait du temps CPU pour un gain négligeable. J'inclus image/svg+xml car les SVG textuels gagnent beaucoup à être compressés. gzip_min_length 256 évite de compresser les micro-fichiers (une requête de 50 octets ne justifie pas l'effort). Le niveau 5 est un bon équilibre entre taux de compression et temps CPU – au-delà, le gain est marginal mais le coûte monte vite.

Si tu utilises Nginx en reverse proxy derrière un autre serveur (par exemple Node.js), ajoute gzip_proxied any;. Cela garantit que même si le backend ne compresse pas, Nginx le fera pour le client final. J'ai déjà perdu des heures à me demander pourquoi la compression ne fonctionnait pas, alors que le backend envoyait déjà du contenu non compressé… Avec cette directive, tout est géré en sortie.

Pour vérifier que ça marche, lance un curl -H "Accept-Encoding: gzip" -I https://ton-domaine et regarde l'en-tête Content-Encoding: gzip. Tu dois aussi voir Vary: Accept-Encoding si gzip_vary est activé. Si rien ne change, regarde les logs ou teste avec un fichier CSS/JS suffisamment gros (au-dessus de gzip_min_length).

Dernier conseil : ne touche pas aux paramètres de niveau de compression par défaut. 4-5 suffisent largement. J'ai vu des développeurs mettre 9, pensant économiser plus, mais ça ralentit le serveur pour un gain supplémentaire dérisoire. Active aussi gzip_static on; si tu peux pré-compresser les fichiers statiques (par exemple avec un module comme ngx_http_gzip_static_module), ça évite de compresser à la volée. Fais simple, teste avec curl et observe les économies de bande passante. C'est la première étape d'une config scalable.

Mettre en place la mise en cache des fichiers statiques

La mise en cache navigateur est l'optimisation la plus rentable pour Nginx. Quelques lignes de config et tu réduis ta charge serveur de 80 %, sans toucher une ligne de code. Sur un de mes projets, le nombre de requêtes vers les fichiers statiques est passé de 1,2 million à 120 000 par jour. Le temps de réponse moyen est tombé de 200 ms à 30 ms.

Le principe est simple : quand un navigateur télécharge un fichier statique (CSS, JS, image), il peut le conserver localement pendant une durée que tu définis via les headers HTTP Cache-Control et Expires. Le serveur n'a plus à le renvoyer à chaque visite. C'est la base de la réduction de bande passante.

Voici la configuration que j'utilise systématiquement dans un bloc location :

expires 30d ajoute les headers Cache-Control: max-age=2592000 et Expires. public autorise les caches intermédiaires (proxy, CDN) à stocker la réponse. immutable indique au navigateur que le fichier ne changera jamais pour cette URL : pas besoin de revalidation. no-transform empêche les intermédiaires de modifier ton contenu (ex : décompresser une image). J'ajoute aussi access_log off pour ne pas saturer les logs avec des requêtes statiques.

Attention : ce cache est efficace seulement si tes fichiers ont un nom unique à chaque mise à jour. Utilise un hash dans le nom du fichier (style.abc123.css). Sinon tu risques de servir une version obsolète. C'est la règle d'or : versionnement ou cache court. Avec cette astuce, tu peux mettre des durées très longues (un an) sans souci.

Teste avec curl -I https://tonsite.com/style.css pour vérifier les headers. Tu dois voir Cache-Control: max-age=2592000, public, immutable, no-transform. Combine ça avec la compression gzip vue plus haut pour des gains supplémentaires. Sur un site à fort trafic, cette config te fera économiser des serveurs entiers.

Configurer Nginx en reverse proxy et load balancing

Si tu penses que Nginx ne sert qu'à servir des fichiers statiques, tu passes à côté de l'essentiel. Nginx excelle comme reverse proxy et load balancer, et c'est là que tu vas gagner en scalabilité. Sans lui, ta seule option serait de coller un HAProxy devant ton app, mais Nginx fait très bien le job tout seul, avec une intégration directe dans ta configuration existante.

Commence par configurer un reverse proxy simple. Dans la section http {} de nginx.conf, ajoute un bloc upstream qui pointe vers ton serveur applicatif. Ensuite, utilise proxy_pass dans un bloc location pour rediriger le trafic. Voici le minimum vital :

Ça marche, mais si le backend tombe, Nginx renvoie une erreur. Pour améliorer la scalabilité, ajoute un deuxième serveur dans l'upstream. Par défaut, Nginx répartit les requêtes en round-robin. Tu peux aussi spécifier des poids avec weight si un serveur est plus puissant que l'autre :

Le load balancing ne se limite pas au round-robin. Mon avis : dans la majorité des cas, least_conn est plus efficace. Elle envoie la requête au serveur qui a le moins de connexions actives, ce qui évite de saturer un endpoint lent. Si tu as besoin de sessions persistantes (exemple : panier d'achat), active ip_hash pour qu'un client soit toujours redirigé vers le même backend. Mais attention : si un serveur tombe, sa session est perdue, donc prévois un plan B du côté applicatif.

N'oublie pas les health checks passifs : avec max_fails et fail_timeout, Nginx marque un serveur comme défaillant après un certain nombre d'échecs et ne lui envoie plus de trafic pendant un temps. Exemple concret :

Tu peux aussi ajouter l'option backup sur un serveur pour qu'il ne soit utilisé qu'en cas de panne des principaux. Avec ces directives, tu transformes Nginx en véritable passerelle résiliente. Teste toujours avec un nginx -t avant de recharger la configuration. La scalabilité ne dépend pas que du nombre de workers, elle repose aussi sur une bonne architecture de routage en amont.

Conclusion

Si tu arrives ici, c'est que tu as déjà appliqué les optimisations de base. Honnêtement, avec les worker_processes ajustés, la compression gzip active, le cache des fichiers statiques et un reverse proxy bien configuré, tu as déjà 80% du chemin vers une infrastructure scalable. Sur un projet récent, j'ai appliqué ces quatre leviers sur un serveur Nginx qui servait 500 requêtes/seconde. Résultat : le temps de réponse moyen est passé de 200 ms à 45 ms, et le nombre de connexions simultanées supportées a été multiplié par 4. Sans compter la bande passante économisée grâce à gzip (500 ko → 150 ko par page). Ce genre de gains n'est pas rare.

La combinaison est simple mais redoutable : des workers qui exploitent tous les cœurs, une compression qui divise le trafic, un cache statique qui évite de marteler le disque, et un reverse proxy qui distribue la charge et met en cache les réponses dynamiques. Là où beaucoup complexifient avec des solutions tierces, je te conseille de maîtriser ces mécanismes de base. Ils suffisent dans 90% des cas. Par exemple, la directive proxy_cache que j'ai montrée permet de réduire la charge sur tes serveurs amont de 70% sans une ligne de code supplémentaire. C'est le genre d'optimisation pragmatique qui change tout.

Bien sûr, il y aura toujours des cas spécifiques : API temps réel, websockets, streaming vidéo. Mais avant d'ajouter un cache Redis ou un équilibreur de charge applicatif, vérifie que tu utilises correctement ces leviers native de Nginx. Trop souvent, je vois des développeurs empiler des couches de complexité alors qu'une simple règle de cache bien pensée aurait résolu le problème.

Dernier conseil : ne règle pas tout en aveugle. Mesure d'abord avec nginx -T pour valider la config, puis charge avec ab ou wrk. Et surtout, surveille les logs d'erreur. Si tu vois "worker_connections are not enough", tu sais quoi faire. L'optimisation est un processus itératif, pas une fin en soi. Ces quatre pratiques te donnent une base solide pour scaler, alors teste, ajuste et répète jusqu'à ce que ça tienne la charge.

Link_