Ce qu'est nginx, et les cinq rôles qu'il joue
nginx (se prononce « engine-x ») est un serveur et proxy HTTP open source. Igor Sysoev l'a écrit pour garder dix mille connexions simultanées ouvertes sur une seule machine, à une époque où cela faisait tomber la plupart des serveurs, et l'a publié en 2004. C'est aujourd'hui l'un des deux serveurs web les plus déployés au monde, avec Apache, et le moteur derrière de nombreux répartiteurs de charge, contrôleurs d'ingress Kubernetes et CDN. La version open source se trouve sur nginx.org ; F5, qui a racheté la société qui en est à l'origine en 2019, vend une édition commerciale appelée NGINX Plus.
Le nom couvre cinq rôles, et une seule configuration peut les combiner tous :
Serveur web. Il lit des fichiers sur le disque et les envoie : HTML, CSS, JavaScript, images, téléchargements. C'est ce qu'il fait le plus vite, sendfile laissant la copie au noyau.
Reverse proxy. Il accepte la requête et la transmet à une application qui ne devrait pas affronter internet elle-même : PHP-FPM, Node.js, Python, Go, Java, un conteneur. Le sujet complet, avec les en-têtes à transmettre, se trouve dans notre guide du reverse proxy.
Répartiteur de charge. Il répartit les requêtes entre plusieurs copies de cette application et arrête d'envoyer du trafic à celle qui tombe en panne.
Cache. Il stocke les réponses de l'upstream sur disque et répond aux requêtes répétées sans redemander à l'application.
Terminateur TLS. Il détient les certificats, parle HTTPS, HTTP/2 et HTTP/3 au navigateur, et parle HTTP en clair à l'application sur une adresse privée.
Ce qu'il n'est pas : un serveur d'applications. nginx n'exécute pas votre code PHP, Python ou Ruby. Il transmet ces requêtes à un processus qui le fait, et renvoie la réponse.
Événementiel : pourquoi nginx tient autant de connexions
Au démarrage de nginx, un processus master lit la configuration, ouvre les ports d'écoute et démarre quelques processus worker, généralement un par cœur de CPU (worker_processes auto). Le master ne sert jamais de trafic. Il gère les workers, et c'est ce qui rend un reload fluide.
Chaque worker est mono-thread et exécute une boucle d'événements. Il demande au noyau, via epoll sur Linux ou kqueue sur BSD et macOS, lesquelles de ses connexions ont quelque chose de prêt : une nouvelle requête, un client qui peut recevoir plus d'octets, un upstream qui a répondu. Il effectue un petit travail sur chaque connexion prête et passe à la suivante ; rien ne reste en attente. Une connexion keep-alive inactive entre deux requêtes, ou un client mobile lent qui envoie sa requête au ralenti, coûte au worker quelques kilo-octets de mémoire et aucun CPU.
Le modèle classique d'Apache est l'inverse. Le MPM prefork donne à chaque connexion son propre processus et le MPM worker son propre thread, et ce processus ou ce thread reste occupé pendant toute la durée de la connexion, qu'elle soit active ou inactive. Dix mille connexions keep-alive ouvertes signifient dix mille processus ou threads, avec la mémoire et les changements de contexte que cela implique. Le MPM event, plus récent chez Apache, place les connexions keep-alive inactives sous la garde d'un thread listener, ce qui réduit l'écart, mais chaque requête active occupe encore un thread.
La règle qui en découle : un worker ne doit jamais bloquer. Le travail qui prend réellement du temps, comme exécuter du PHP ou interroger une base de données, se passe dans un autre processus, et nginx traite la réponse comme un événement de plus. La capacité totale est worker_processes × worker_connections, et quand nginx fait du proxy, chaque client utilise deux de ces emplacements : un vers le navigateur, un vers l'upstream.
Le début de nginx.conf : un master, un worker par cœur, une boucle d'événements dans chacun
user www-data;
worker_processes auto; # one worker per CPU core
error_log /var/log/nginx/error.log warn;
events {
worker_connections 1024; # per worker: clients + upstream connections
}
http {
include mime.types;
sendfile on;
keepalive_timeout 65s;
include /etc/nginx/conf.d/*.conf; # one file per site
}
À quoi sert nginx
En pratique, nginx occupe l'une de ces positions, souvent plusieurs à la fois.
Servir un site statique ou un build front-end. Un build React, Vue ou Astro, de la documentation, une landing page : des fichiers sur disque et nginx devant, rien d'autre.
Devant PHP. WordPress, Laravel et la plupart des applications PHP tournent avec nginx plus PHP-FPM. nginx sert lui-même les images, le CSS et le JavaScript et transmet les requêtes .php à FPM via un socket Unix avec fastcgi_pass.
Devant un serveur d'applications. Les services Node.js, Python (Gunicorn, Uvicorn), Ruby (Puma) et Go écoutent sur un port local. nginx termine le HTTPS, absorbe les clients lents en mettant la requête en tampon, puis la transmet avec proxy_pass, si bien que l'application ne voit jamais que des requêtes rapides et complètes.
La répartition de charge entre plusieurs instances de l'application, couverte brièvement plus bas.
TLS et protocoles modernes pour une application qui n'en a aucun : certificats (la plupart les obtiennent avec certbot et Let's Encrypt), HTTP/2 avec http2 on; et HTTP/3 avec listen 443 quic; sur nginx 1.25 et versions ultérieures. Ce que ces protocoles changent est expliqué dans HTTP/2 vs HTTP/3.
Des règles à la porte : redirections, limitation de débit avec limit_req, listes d'autorisation et de refus d'IP, compression gzip et en-têtes de réponse. Quels en-têtes envoyer, et pourquoi, dans en-têtes de sécurité HTTP.
Un server block minimal, ligne par ligne
Un bloc server représente un site. nginx le choisit selon le port sur lequel la requête est arrivée et l'en-tête Host, comparé à server_name. À l'intérieur, les blocs location correspondent aux chemins d'URL. Le bloc ci-dessous sert un site statique et transmet /api/ à une application sur le port 3000.
listen est le port, une fois pour IPv4 et une fois pour IPv6.
server_name liste les noms d'hôte à qui ce bloc répond. Une requête dont le Host ne correspond à aucun bloc va vers le serveur par défaut du port : le bloc marqué default_server, ou sinon le premier que nginx a lu. C'est pourquoi un nom d'hôte inconnu pointé vers votre IP affiche un autre site. Un bloc fourre-tout qui répond return 444; (ferme la connexion) règle le problème.
root indique où chercher les fichiers : /about.html devient /var/www/example/about.html.
try_files essaie le fichier, puis un répertoire, puis le repli. Pour une application monopage, remplacez =404 par /index.html pour que les routes côté client chargent l'application.
La correspondance des location a un ordre qui surprend. Une correspondance exacte (=) gagne d'emblée. Sinon, nginx retient le préfixe correspondant le plus long, puis essaie les expressions régulières (~, ~*) dans l'ordre du fichier et prend la première qui correspond ; ce n'est que si aucune ne correspond que le préfixe retenu est utilisé. Un préfixe ^~ saute l'étape des regex. Dans cet exemple, /api/logo.png est servi par la regex d'images, pas par /api/, ce qui est la réponse habituelle à « pourquoi ma location est-elle ignorée ».
expires fixe Cache-Control: max-age et Expires sur les ressources avec empreinte. Choisissez les valeurs avec le guide Cache-Control.
Ce bloc est en HTTP simple. Ajoutez le certificat avec certbot, qui modifie le bloc pour vous, et redirigez le port 80 vers le 443.
/etc/nginx/conf.d/example.conf : un site statique avec une API derrière
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
access_log /var/log/nginx/example.access.log;
error_log /var/log/nginx/example.error.log;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(?:css|js|woff2|png|jpg|webp|avif|svg)$ {
expires 30d;
access_log off;
}
location /api/ {
proxy_pass http://127.0.0.1:3000;
# plus the forwarded headers from the reverse proxy guide
}
}
Installer, tester, recharger : les commandes du quotidien
Les paquets de distribution placent le fichier principal dans /etc/nginx/nginx.conf. Debian et Ubuntu gardent les sites dans /etc/nginx/sites-available/ et les activent avec un lien symbolique dans sites-enabled/ ; RHEL, Rocky, Alpine et les paquets nginx.org lisent /etc/nginx/conf.d/*.conf. Les logs vont dans /var/log/nginx/.
Deux habitudes évitent la plupart des pannes auto-infligées. Lancez nginx -t avant chaque reload : cela analyse toute la configuration et indique le fichier et la ligne de toute erreur. Et rechargez plutôt que redémarrez. Lors d'un reload, le master démarre de nouveaux workers avec la nouvelle configuration et laisse les anciens terminer leurs requêtes ouvertes, si bien qu'aucune connexion n'est coupée ; si la nouvelle configuration ne se charge pas, le master garde les anciens workers en marche et consigne pourquoi.
nginx -T affiche la configuration complète telle que nginx la voit, chaque include développé. Quand un réglage semble sans effet, c'est généralement qu'il est écrasé dans un fichier que vous avez oublié, et -T vous montre lequel.
# Debian / Ubuntu
sudo apt install nginx
nginx -v # version; -V adds build options and modules
# check the syntax, then apply without dropping connections
sudo nginx -t && sudo systemctl reload nginx
# the whole effective configuration, includes expanded
sudo nginx -T | less
# which names and ports are configured
sudo nginx -T | grep -E '^\s*(server_name|listen)'
# watch errors while you test
sudo tail -f /var/log/nginx/error.log
nginx contre Apache
Les deux sont matures, gratuits, et assez rapides pour presque n'importe quel site. Les différences qui décident réellement entre eux :
Modèle de connexion. La boucle d'événements de nginx tient les connexions inactives et lentes presque gratuitement ; Apache lie un processus ou un thread à chaque requête active et, hors MPM event, à chaque connexion inactive aussi. Avec de nombreuses connexions keep-alive simultanées, nginx utilise bien moins de mémoire.
Configuration. Avec AllowOverride activé, Apache lit les fichiers .htaccess de chaque répertoire sur le chemin de chaque requête, si bien qu'un utilisateur peut changer des règles sans toucher la configuration du serveur. nginx n'a pas d'équivalent : chaque règle vit dans la configuration centrale et n'est analysée qu'une fois, au chargement. C'est plus rapide et plus facile à auditer, mais un .htaccess WordPress ou Laravel doit être réécrit en règles location et try_files lors d'une migration.
PHP. Apache peut exécuter PHP dans ses propres processus avec mod_php ; nginx parle toujours à un pool PHP-FPM séparé. Les configurations Apache actuelles utilisent souvent PHP-FPM aussi, donc c'est désormais surtout une différence de réglages par défaut.
Modules. Apache charge des modules à l'exécution depuis un très grand catalogue. nginx prend en charge les modules dynamiques, mais chacun doit être compilé pour la version exacte de nginx que vous utilisez, donc de nombreux extras signifient de la compilation.
Les fichiers statiques et le proxying sont là où nginx est le plus fort. C'est pourquoi un hybride courant met nginx devant, servant les fichiers et terminant le TLS, avec Apache derrière exécutant une application qui dépend de .htaccess.
Le résumé honnête : en partant de zéro, nginx est le choix par défaut pour la plupart des équipes. Remplacer un Apache qui fonctionne ne rendra cependant pas un site lent rapide. La partie lente, c'est presque toujours l'application ou la distance jusqu'au visiteur, pas le serveur web.
Reverse proxy, répartiteur de charge et cache : la version courte
Le proxying est une seule directive, proxy_pass, plus les en-têtes qui disent à l'application qui est vraiment le client. Ces en-têtes, le support WebSocket et proxy_cache sont couverts dans le guide du reverse proxy, donc cette page ne les répète pas.
La répartition de charge ajoute un bloc upstream qui nomme les serveurs. Le défaut est le round robin. least_conn envoie chaque requête au serveur avec le moins de connexions actives, ce qui convient aux requêtes de durée inégale, et ip_hash ou hash garde un client sur le même serveur. Le contrôle de santé dans nginx open source est passif : après max_fails échecs en fail_timeout, un serveur est écarté pendant cette durée, et proxy_next_upstream retente la requête sur un autre. Les contrôles actifs qui sondent une URL selon un planning sont une fonctionnalité de NGINX Plus. Pour la vue d'ensemble, voir ce que fait un répartiteur de charge.
Le cache fonctionne bien, avec une lacune à connaître d'emblée : nginx open source n'a aucune commande pour purger une URL en cache. Vous attendez l'expiration de l'entrée ou supprimez les fichiers du répertoire de cache sur chaque serveur. Cette lacune est l'une des raisons les plus courantes pour lesquelles les équipes déplacent le cache public vers un CDN.
Trois serveurs d'application derrière un seul nginx
upstream app {
least_conn;
server 10.0.0.11:3000 max_fails=3 fail_timeout=10s;
server 10.0.0.12:3000 max_fails=3 fail_timeout=10s;
server 10.0.0.13:3000 backup; # used only when the others are down
keepalive 32; # reuse connections to the app
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection ""; # required for upstream keepalive
proxy_next_upstream error timeout http_502;
# plus the forwarded headers from the reverse proxy guide
}
}
502, 504 et 413 : ce que nginx vous dit
Le code de statut que voit le visiteur est le résumé ; le journal d'erreurs est l'explication. Chacune de ces erreurs y laisse une ligne caractéristique.
502 Bad Gateway signifie que nginx n'a reçu aucune réponse utilisable de l'upstream. connect() failed (111: Connection refused) indique que rien n'écoute à l'adresse indiquée dans proxy_pass : l'application est arrêtée ou sur un autre port. connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory) est un chemin de socket PHP-FPM qui ne correspond pas à la version de PHP installée, et (13: Permission denied) sur la même ligne est un socket que l'utilisateur de nginx ne peut pas ouvrir. upstream prematurely closed connection signifie que l'application a planté ou a été tuée en cours de requête ; lisez son propre journal et les messages d'out-of-memory du noyau. upstream sent too big header signifie que les en-têtes de réponse, souvent un tas de cookies, ne rentrent pas dans le tampon : augmentez proxy_buffer_size, ou fastcgi_buffer_size pour PHP. Le diagnostic complet, y compris le cas avec un CDN devant, est dans 502 Bad Gateway.
504 Gateway Timeout signifie que l'upstream a accepté la requête et n'a pas répondu dans le délai proxy_read_timeout (ou fastcgi_read_timeout), 60 secondes par défaut. Le journal indique upstream timed out (110: Connection timed out) while reading response header from upstream. Un délai plus long convient à un export ou un rapport connu pour être lent ; pour des pages ordinaires, cela ne fait que masquer une requête lente ou un pool de workers plein. Chaque proxy de la chaîne a sa propre limite, donc un répartiteur de charge ou un CDN devant peut abandonner avant nginx.
413 Request Entity Too Large signifie que le corps de la requête dépasse client_max_body_size, qui est de 1 Mo par défaut : le classique échec d'envoi d'image ou de sauvegarde. Le journal indique client intended to send too large body. Augmentez la limite dans le server ou la location qui reçoit les envois plutôt que globalement, et pour PHP augmentez aussi upload_max_filesize et post_max_size en conséquence, sinon PHP rejette ce que nginx a laissé passer.
Un 503 venant de nginx est généralement son propre limit_req ou limit_conn qui refuse une requête ; cela et les autres causes sont dans 503 Service Unavailable. Et directory index of "/var/www/..." is forbidden est un 403 pour un répertoire sans fichier d'index, couvert dans 403 Forbidden.
Lignes du journal d'erreurs et la directive que chacune indique
# /var/log/nginx/error.log (abridged)
connect() failed (111: Connection refused) while connecting to upstream -> 502
upstream prematurely closed connection while reading response header -> 502
upstream sent too big header while reading response header from upstream -> 502
upstream timed out (110: Connection timed out) while reading response header -> 504
client intended to send too large body: 52428800 bytes -> 413
# the matching fixes, in the server or location that needs them
client_max_body_size 64m; # PHP: raise upload_max_filesize and post_max_size too
proxy_read_timeout 300s; # only for a known slow endpoint
proxy_buffer_size 16k; # large response headers and cookies
proxy_buffers 8 16k;
Quand mettre un CDN devant nginx
Un seul nginx sert beaucoup de trafic. Ce qu'il ne peut pas changer, c'est son emplacement. Un visiteur dans un autre pays attend à chaque aller-retour vers votre unique datacentre ; un pic de trafic composé surtout de requêtes répétées atterrit quand même sur votre unique machine ; une attaque visant votre IP atteint le serveur qui fait tourner votre site. Ce sont les moments pour ajouter un CDN : votre audience est loin du serveur, le trafic cachable représente une large part de la charge, vous avez besoin de purger le cache sur plus d'une machine, ou l'origine ne devrait plus être directement accessible.
Vous gardez nginx ; son rôle change. Le CDN devient la porte d'entrée publique et nginx devient l'origine, servant les fichiers et routant vers l'application tandis que la périphérie gère la distance, la mise en cache et le filtrage. Trois choses à ajuster côté nginx. Envoyez des en-têtes Cache-Control corrects, parce que la périphérie les suit. Restaurez l'IP du visiteur avec le module realip (set_real_ip_from pour les adresses du CDN, real_ip_header pour l'en-tête qu'il envoie), sinon chaque ligne de journal et chaque zone limit_req voit la périphérie au lieu du visiteur. Et n'autorisez que le CDN à atteindre l'origine, pour que les attaques ne puissent pas la contourner.
La périphérie de CDN.com.tr tourne elle-même sur nginx : sa configuration applique la taille de fichier de votre forfait comme client_max_body_size, donc un envoi qui passe par le CDN a besoin que cette limite soit également assez grande chez vous. Devant votre origine, le réseau de périphérie, avec des serveurs en Turquie et à l'étranger, met en cache selon des règles de diffusion par chemin et purge instantanément par chemin exact ou dossier depuis le panneau, la ligne de commande cdnctl ou l'API REST. Un WAF, une protection DDoS, une protection anti-bot avec un défi JavaScript, des règles par pays et par IP et une limitation de débit s'exécutent avant qu'une requête n'atteigne votre serveur, et des certificats Let's Encrypt sont émis et renouvelés pour chaque domaine connecté.
Deux détails facilitent la vérification du changement. Les pages déjà dans le cache de périphérie continuent d'être servies pendant que votre origine est en panne, et l'en-tête de réponse X-Proxy-Cache-MT indique HIT ou MISS, donc vous pouvez savoir si une requête a atteint votre nginx ou non. La périphérie envoie aussi le SNI à l'origine, donc un nginx avec plusieurs blocs server HTTPS présente le bon certificat.
FAQ nginx
Comment prononcer nginx ?
« Engine-x ». On rencontre aussi bien nginx que NGINX ; la forme en minuscules est le nom du projet open source et de son binaire.
nginx est-il gratuit ?
Le nginx open source de nginx.org est gratuit sous licence BSD à deux clauses, même pour un usage commercial. NGINX Plus est un abonnement payant de F5 qui ajoute des contrôles de santé actifs, une API pour purger le cache et changer les upstreams à l'exécution, un tableau de bord d'état en direct et le support.
nginx est-il un serveur web ou un reverse proxy ?
Les deux, souvent dans la même configuration. Une location avec root sert des fichiers depuis le disque, une avec proxy_pass ou fastcgi_pass transmet la requête à une application. La plupart des sites font les deux : les ressources statiques directement, tout le dynamique via le proxy.
nginx est-il meilleur qu'Apache ?
Pour les fichiers statiques, le proxying et un grand nombre de connexions simultanées, il utilise moins de mémoire, et c'est le choix habituel pour les nouvelles configurations. Apache convient mieux quand vous dépendez de .htaccess ou d'un module propre à Apache. Pour un site typique, aucun des deux serveurs web n'est le goulot d'étranglement ; ce sont l'application et la distance jusqu'aux visiteurs.
Pourquoi mon domaine affiche-t-il un autre site sur nginx ?
Aucun server_name ne correspondait à l'en-tête Host de la requête, donc nginx a utilisé le serveur par défaut de ce port : le bloc marqué default_server, ou le premier qu'il a chargé. Vérifiez les noms avec nginx -T | grep server_name, et ajoutez un bloc fourre-tout qui renvoie 444 pour que les noms d'hôte inconnus n'obtiennent rien.
Ai-je encore besoin de nginx si j'utilise un CDN ?
Généralement oui, en tant qu'origine : quelque chose doit toujours servir les fichiers et router les requêtes vers votre application, et le CDN va les chercher là. Sur les applications conteneur de CDN.com.tr, vous pouvez vous en passer pour la publication, parce que la périphérie atteint chaque application via une route interne de la plateforme, sans conteneur reverse proxy intermédiaire.