
Nginx est un serveur web et reverse proxy ultra-performant. Si vous devez servir un site statique, proxyfier une API, activer HTTPS ou diagnostiquer une erreur 502, ce guide vous donne les recettes essentielles en quelques minutes.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »Niveau : Débutant → Intermédiaire · Durée : 30-45 min
À la fin de ce guide, vous saurez :
- Servir un site statique en 5 minutes
- Configurer un reverse proxy vers une application
- Activer HTTPS avec Let's Encrypt (TLS 1.2/1.3)
- Diagnostiquer les erreurs courantes (502, 504, 403)
- Sécuriser et optimiser votre configuration
Choisissez votre parcours
Section intitulée « Choisissez votre parcours »Les six entrées ci-dessous mènent chacune à une section autonome. Un serveur monté de zéro suit l'ordre site statique, reverse proxy, puis HTTPS. Une installation déjà en place se consulte par symptôme : partez du 502 ou du 403 que vous observez, le diagnostic renvoie ensuite vers la directive en cause.
Nginx en 60 secondes
Section intitulée « Nginx en 60 secondes »Nginx (prononcé "engine-x") est à la fois :
- Un serveur web pour fichiers statiques (HTML, CSS, JS, images)
- Un reverse proxy pour transmettre les requêtes à des applications backend
- Un load balancer pour répartir le trafic entre plusieurs serveurs
- Un cache pour accélérer les réponses
Installation
Section intitulée « Installation »Les paquets fournis par les distributions retardent souvent de plusieurs versions mineures. Les commandes ci-dessous déclarent le dépôt officiel nginx.org, dont la clé de signature est vérifiée par le gestionnaire de paquets, ce qui donne accès à la branche stable courante. Le service démarre avec une page d'accueil par défaut : elle sert à confirmer l'écoute sur le port 80 avant d'écrire la moindre configuration de site.
# Ajouter le dépôt officiel Nginx (version récente)sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyringcurl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \ | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list
# Installersudo apt update && sudo apt install nginx
# Démarrer et activer au bootsudo systemctl enable --now nginx# Ajouter le dépôt officiel Nginxsudo tee /etc/yum.repos.d/nginx.repo << 'EOF'[nginx-stable]name=nginx stable repobaseurl=http://nginx.org/packages/rhel/$releasever/$basearch/gpgcheck=1enabled=1gpgkey=https://nginx.org/keys/nginx_signing.keyEOF
# Installersudo dnf install nginx
# Démarrer et activer au bootsudo systemctl enable --now nginxVérification : ouvrez http://votre-ip dans un navigateur, vous devez voir la page "Welcome to nginx".
# Vérifier que Nginx tournesystemctl status nginx# active (running) ✓
# Vérifier la versionnginx -v# nginx version: nginx/1.26.xServir un site statique
Section intitulée « Servir un site statique »C'est le cas d'usage le plus simple : afficher des fichiers HTML/CSS/JS.
-
Créer le répertoire et le fichier
Fenêtre de terminal sudo mkdir -p /var/www/monsiteecho '<h1>Hello Nginx!</h1>' | sudo tee /var/www/monsite/index.html -
Créer la configuration du site
Fenêtre de terminal sudo tee /etc/nginx/conf.d/monsite.conf << 'EOF'server {listen 80;server_name monsite.local;root /var/www/monsite;index index.html;location / {try_files $uri $uri/ =404;}}EOF -
Tester et recharger la configuration
Fenêtre de terminal # Toujours tester AVANT de rechargersudo nginx -t# nginx: configuration file /etc/nginx/nginx.conf test is successfulsudo systemctl reload nginx -
Vérifier
Fenêtre de terminal curl -I http://localhost# HTTP/1.1 200 OK
Configurer un reverse proxy
Section intitulée « Configurer un reverse proxy »Le reverse proxy place Nginx devant une application qui écoute sur un port non privilégié. Nginx termine le TLS, applique le cache et les limites de débit, puis relaie la requête accompagnée des en-têtes X-Forwarded-* qui décrivent le client d'origine. Cette partie couvre la configuration de base, l'ajustement des timeouts pour les traitements longs, et le cas particulier des WebSockets qui exigent un changement de protocole.
Pourquoi utiliser un reverse proxy ?
Section intitulée « Pourquoi utiliser un reverse proxy ? »Un reverse proxy est un intermédiaire entre vos utilisateurs et votre application. Sans lui, votre app serait exposée directement sur Internet.
Scénario concret : Vous avez une API Node.js qui tourne sur localhost:3000. Vous voulez :
- Qu'elle soit accessible sur
api.example.com(pasexample.com:3000) - Qu'elle utilise HTTPS (votre app Node ne gère pas TLS)
- Que les logs HTTP soient centralisés
- Pouvoir ajouter du rate limiting, du cache, etc.
Le reverse proxy fait tout ça. Votre app reste simple, Nginx gère la complexité HTTP.
| Question | Réponse |
|---|---|
| "Pourquoi pas exposer mon app directement ?" | Sécurité (TLS), flexibilité (plusieurs apps), performances (cache) |
| "Pourquoi Nginx et pas mon framework ?" | Nginx est optimisé pour ça (C10K), votre app pour la logique métier |
| "Quand est-ce inutile ?" | Développement local, ou si vous utilisez un PaaS (Heroku, Vercel) |
Configuration de base
Section intitulée « Configuration de base »Sans les quatre directives proxy_set_header, le backend voit toutes les requêtes arriver de 127.0.0.1 en HTTP : les journaux applicatifs deviennent inexploitables et les redirections que l'application génère repartent en clair. La variable $proxy_add_x_forwarded_for complète la chaîne existante au lieu de l'écraser, ce qui compte dès qu'un CDN ou un load balancer se trouve en amont.
server { listen 80; server_name api.example.com;
location / { proxy_pass http://127.0.0.1:3000;
# Headers essentiels pour que le backend connaisse le client réel 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; }}Timeouts pour les backends lents
Section intitulée « Timeouts pour les backends lents »Si votre backend met du temps à répondre (traitement lourd, IA, etc.) :
location / { proxy_pass http://backend:3000;
# Augmenter les timeouts (défaut: 60s) proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_read_timeout 120s;
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;}WebSockets
Section intitulée « WebSockets »Pour les applications temps réel (chat, notifications) :
location /ws/ { proxy_pass http://backend:3000;
# Upgrade de connexion pour WebSocket proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;}Activer HTTPS avec Let's Encrypt
Section intitulée « Activer HTTPS avec Let's Encrypt »Nginx n'obtient pas les certificats lui-même : Certbot dialogue avec Let's Encrypt, dépose les fichiers dans /etc/letsencrypt et modifie les blocs server pour les référencer. Le certificat vaut 90 jours et un timer systemd déclenche le renouvellement bien avant l'échéance. Reste à votre charge le choix des protocoles TLS acceptés, que les valeurs par défaut ne garantissent pas selon la version de Nginx et d'OpenSSL installée.
Pourquoi HTTPS est indispensable
Section intitulée « Pourquoi HTTPS est indispensable »HTTPS n'est plus une option, c'est une obligation :
| Sans HTTPS | Avec HTTPS |
|---|---|
| Les mots de passe transitent en clair | Tout est chiffré |
| Google pénalise votre SEO | Bonus de classement |
| Les navigateurs affichent "Non sécurisé" | Cadenas vert |
| Vulnérable aux attaques man-in-the-middle | Communication sécurisée |
Let's Encrypt est une autorité de certification gratuite. Certbot est l'outil qui automatise l'obtention et le renouvellement des certificats.
-
Installer Certbot
Fenêtre de terminal sudo apt install certbot python3-certbot-nginxFenêtre de terminal sudo dnf install epel-releasesudo dnf install certbot python3-certbot-nginx -
Obtenir le certificat
Fenêtre de terminal sudo certbot --nginx -d example.com -d www.example.comCertbot modifie automatiquement votre configuration Nginx.
-
Vérifier la configuration générée
Certbot crée une configuration similaire à :
server {listen 443 ssl http2;server_name example.com www.example.com;ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;# Forcer TLS 1.2/1.3 (ne pas se fier aux défauts)ssl_protocols TLSv1.2 TLSv1.3;ssl_prefer_server_ciphers on;# HSTS : force HTTPS pendant 1 anadd_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;location / {root /var/www/html;index index.html;}}# Redirection HTTP → HTTPSserver {listen 80;server_name example.com www.example.com;return 301 https://$host$request_uri;} -
Configurer le renouvellement automatique
Fenêtre de terminal # Tester le renouvellementsudo certbot renew --dry-run# Le timer systemd gère le renouvellement automatiquesystemctl list-timers | grep certbot
Comprendre la structure des fichiers
Section intitulée « Comprendre la structure des fichiers »La configuration Nginx est organisée en plusieurs fichiers :
/etc/nginx/├── nginx.conf # Config principale (global)├── conf.d/ # Vos sites (1 fichier par site)│ ├── default.conf│ └── monsite.conf├── mime.types # Types MIME└── modules-enabled/ # Modules dynamiques (Debian)Le fichier principal nginx.conf
Section intitulée « Le fichier principal nginx.conf »Ce fichier ne décrit aucun site : il fixe le contexte dans lequel tous les blocs server s'exécuteront. Deux directives dimensionnent le serveur, worker_processes et worker_connections, dont le produit borne le nombre de connexions simultanées. server_tokens off retire le numéro de version des réponses et de la page d'erreur. La ligne include finale est ce qui rend actifs les fichiers déposés dans conf.d/.
user www-data; # Utilisateur des workersworker_processes auto; # 1 worker par CPUpid /run/nginx.pid;
events { worker_connections 1024; # Connexions simultanées par worker}
http { # Performances sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65;
# Types MIME include /etc/nginx/mime.types; default_type application/octet-stream;
# Logs access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log;
# Compression gzip on; gzip_types text/plain text/css application/json application/javascript;
# Masquer la version de Nginx server_tokens off;
# Inclure les configurations des sites include /etc/nginx/conf.d/*.conf;}Dépannage express
Section intitulée « Dépannage express »Le dépannage Nginx suit toujours le même schéma : est-ce que le problème vient de la config ? Du backend ? Des permissions ? Des logs ?
Avec les bonnes commandes, vous identifiez la cause en 30 secondes.
Pour les erreurs côté certificat (chaîne incomplète, mauvais SNI, expiration), appuyez-vous sur le guide diagnostic TLS avec openssl.
La commande magique : nginx -t
Section intitulée « La commande magique : nginx -t »Toujours tester la configuration avant de recharger :
sudo nginx -t# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok# nginx: configuration file /etc/nginx/nginx.conf test is successfulSi erreur, Nginx indique la ligne exacte du problème.
Diagnostic en 30 secondes
Section intitulée « Diagnostic en 30 secondes »L'ordre de ces commandes remonte du processus vers la requête, ce qui évite de chercher une erreur de configuration alors que le service est arrêté. nginx -T se distingue de nginx -t en affichant la configuration effectivement chargée, tous les include résolus : c'est ce qui révèle un bloc server en doublon ou un fichier oublié dans conf.d/.
# 1. Nginx tourne-t-il ?systemctl status nginx
# 2. La config est-elle valide ?sudo nginx -t
# 3. Dump complet de la config (includes résolus)sudo nginx -T | less
# 4. Logs récents (systemd)journalctl -u nginx --since "10 min ago"
# 5. Logs d'erreur Nginxsudo tail -20 /var/log/nginx/error.log
# 6. Le port est-il écouté ?ss -tlnp | grep :80Erreurs courantes et solutions
Section intitulée « Erreurs courantes et solutions »La distinction utile passe entre les erreurs que Nginx produit lui-même et celles qu'il subit. Les codes 403 et 404 viennent du système de fichiers local, donc des permissions ou de la directive root. Les codes 502 et 504 viennent du backend, et aucune modification de la configuration Nginx ne les corrigera si l'application ne répond pas.
| Erreur | Cause probable | Solution |
|---|---|---|
| 403 Forbidden | Permissions fichiers | sudo chown -R www-data:www-data /var/www/ |
| 404 Not Found | Mauvais root ou fichier absent | Vérifier le chemin dans la config |
| 502 Bad Gateway | Backend ne répond pas, ou SELinux bloque la connexion sortante | Lire l'errno dans error.log : il départage les deux cas (voir ci-dessous) |
| 504 Gateway Timeout | Backend trop lent | Augmenter proxy_read_timeout |
| "Address already in use" | Port déjà utilisé | ss -tlnp | grep :80 pour identifier |
Débugger une 502
Section intitulée « Débugger une 502 »Rejouer la requête sous le compte www-data est l'étape qui tranche : si curl réussit en root et échoue sous le compte de service, le problème n'est pas l'application mais les droits ou SELinux.
Ce qui départage les deux causes, c'est le numéro d'errno entre parenthèses dans error.log. Une même 502 en recouvre deux, radicalement différentes :
Ligne dans error.log | Cause réelle | Correction |
|---|---|---|
connect() … failed (111: Connection refused) | Le backend n'écoute pas | Démarrer l'application, vérifier son port |
connect() … failed (13: Permission denied) | SELinux bloque la connexion sortante de nginx | sudo setsebool -P httpd_can_network_connect on |
Le 13 est le piège : en Enforcing, nginx n'a pas le droit d'ouvrir une connexion réseau tant que le booléen httpd_can_network_connect n'est pas posé, et rien dans la page d'erreur ne le laisse deviner. Ce couple d'errno est tout le diagnostic.
# 1. Le backend tourne-t-il ?curl -I http://127.0.0.1:3000# Si "Connection refused" → le backend est down
# 2. Nginx peut-il joindre le backend ?sudo -u www-data curl http://127.0.0.1:3000# Parfois c'est un problème de permissions/SELinux
# 3. Logs détailléssudo tail -f /var/log/nginx/error.log# "connect() failed (111: Connection refused)"Optimisation et cache
Section intitulée « Optimisation et cache »Trois leviers indépendants agissent sur le temps de réponse perçu. La compression gzip réduit le volume transféré sur les contenus texte, le cache proxy évite de solliciter le backend pour une réponse déjà servie, et le load balancing répartit ce qui reste entre plusieurs instances. Ils se cumulent, mais le cache introduit un décalage entre le contenu réel et le contenu servi qu'il faut assumer et surveiller.
Compression gzip
Section intitulée « Compression gzip »Réduit la taille des réponses de 60-80% :
http { gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/javascript application/json application/javascript application/xml image/svg+xml;}Cache pour reverse proxy
Section intitulée « Cache pour reverse proxy »Stocke les réponses du backend pour réduire sa charge :
# Définir la zone de cache (dans http {})proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;
server { location / { proxy_pass http://backend:3000; proxy_cache my_cache; proxy_cache_valid 200 10m; # Cache les 200 pendant 10 min proxy_cache_valid 404 1m; # Cache les 404 pendant 1 min
# Header pour debug add_header X-Cache-Status $upstream_cache_status; }}Load balancing
Section intitulée « Load balancing »Répartir le trafic entre plusieurs backends :
upstream backend_pool { # Round-robin par défaut server backend1.example.com:3000; server backend2.example.com:3000; server backend3.example.com:3000 backup; # Utilisé si les autres sont down}
server { location / { proxy_pass http://backend_pool; proxy_set_header Host $host; }}Algorithmes disponibles :
upstream backend_pool { least_conn; # Envoie au serveur le moins chargé # ou ip_hash; # Même client → même serveur (sessions)
server backend1:3000; server backend2:3000;}Sécurisation
Section intitulée « Sécurisation »Un serveur exposé sur Internet reçoit du trafic automatisé dès ses premières heures de vie. Les quatre mesures qui suivent couvrent des risques distincts : les en-têtes de sécurité contraignent le comportement du navigateur, le rate limiting absorbe les rafales de requêtes, les listes allow/deny ferment les chemins d'administration, et limit_except restreint les méthodes HTTP acceptées.
Headers de sécurité
Section intitulée « Headers de sécurité »Le mot-clé always compte autant que l'en-tête lui-même : sans lui, Nginx omet l'en-tête sur les réponses d'erreur, précisément celles qu'un attaquant cherche à provoquer. Second point non intuitif, l'héritage n'est pas cumulatif : un seul add_header déclaré dans un bloc location annule tous ceux du bloc server parent, qu'il faut alors répéter.
server { # Empêche le clickjacking add_header X-Frame-Options "SAMEORIGIN" always;
# Empêche le sniffing MIME add_header X-Content-Type-Options "nosniff" always;
# Politique de référent add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# HSTS (après avoir vérifié que HTTPS fonctionne) add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# CSP basique (à adapter selon votre app) add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;}Rate limiting
Section intitulée « Rate limiting »Protéger contre les abus et attaques DDoS légères :
# Définir la zone de limite (dans http {})limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server { location /api/ { # Max 10 req/s, burst de 20 avec délai limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend:3000; }}Restreindre l'accès à /admin
Section intitulée « Restreindre l'accès à /admin »Les directives allow et deny sont évaluées dans l'ordre d'écriture et la première correspondance s'applique, d'où le deny all placé en dernier. Elles portent sur l'adresse que Nginx voit réellement : derrière un load balancer ou un CDN, cette adresse est celle de l'équipement amont, et le filtrage laisse alors passer tout le monde.
location /admin/ { # Autoriser uniquement certaines IP allow 192.168.1.0/24; allow 10.0.0.0/8; deny all;
proxy_pass http://backend:3000;}Limiter les méthodes HTTP
Section intitulée « Limiter les méthodes HTTP »limit_except se lit à l'envers de ce que son nom suggère : les méthodes listées sont celles qui échappent aux règles du bloc, toutes les autres reçoivent le deny all. Mentionner GET autorise implicitement HEAD, il est donc inutile de l'ajouter. Cette restriction bloque notamment les requêtes PUT et DELETE émises contre un serveur de fichiers statiques.
location / { # Autoriser seulement GET, POST, HEAD limit_except GET POST HEAD { deny all; }
root /var/www/html;}Monitoring
Section intitulée « Monitoring »La version open source n'expose qu'un jeu de compteurs minimal, via le module stub_status : connexions actives, requêtes acceptées et traitées, répartition entre lecture, écriture et attente. Ces valeurs alimentent sans difficulté un exporter Prometheus et suffisent à suivre la charge, mais elles ne détaillent ni les codes de réponse ni les temps par route, informations qui restent à extraire des journaux d'accès.
Activer le module status
Section intitulée « Activer le module status »Le module stub_status est compilé dans les paquets officiels, il n'y a rien à installer. L'endpoint doit rester limité à la boucle locale : il expose le volume de trafic du serveur et n'a aucune raison d'être joignable depuis l'extérieur, l'exporter tournant lui-même sur la machine ou sur le même hôte Docker.
server { location /nginx_status { stub_status on; allow 127.0.0.1; deny all; }}curl http://127.0.0.1/nginx_status# Active connections: 42# server accepts handled requests# 1234567 1234567 9876543# Reading: 0 Writing: 1 Waiting: 41Prometheus + nginx-exporter
Section intitulée « Prometheus + nginx-exporter »Pour un monitoring complet, utilisez nginx-prometheus-exporter :
# Option 1 : --network host (Linux)docker run --network host nginx/nginx-prometheus-exporter:latest \ -nginx.scrape-uri=http://127.0.0.1/nginx_status
# Option 2 : IP explicite du hostdocker run -p 9113:9113 nginx/nginx-prometheus-exporter:latest \ -nginx.scrape-uri=http://192.168.1.10/nginx_statusPuis configurez Prometheus pour scraper localhost:9113/metrics et visualisez dans Grafana.
Cinq comportements qui surprennent
Section intitulée « Cinq comportements qui surprennent »Les cinq questions ci-dessous portent sur les points où Nginx se comporte autrement qu'attendu : l'ordre d'évaluation des blocs location, la différence entre root et alias, les échecs de proxy_pass, le rechargement sans coupure et l'hébergement de plusieurs sites sur une même machine.
Pourquoi ma location regex ne matche pas ?
Nginx évalue les location dans un ordre précis :
=(exact) → priorité absolue^~(préfixe prioritaire) → stoppe la recherche regex~/~*(regex) → première regex qui matche- préfixe simple → le plus long gagne
location = /api { } # Seulement /api exactlocation ^~ /static/ { } # /static/* sans chercher regexlocation ~ \.php$ { } # Regex : fichiers .phplocation / { } # Catch-allPiège courant : une location préfixe / capture tout avant que votre regex ne soit évaluée. Utilisez ^~ si vous voulez bloquer les regex sur un chemin.
Quelle est la différence entre root et alias ?
root: le chemin de l'URI est ajouté au rootalias: le chemin de l'URI est remplacé par l'alias
# Avec root : /images/photo.jpg → /var/www/images/photo.jpglocation /images/ { root /var/www;}
# Avec alias : /images/photo.jpg → /data/photos/photo.jpglocation /images/ { alias /data/photos/;}Règle simple : utilisez root par défaut, alias seulement si vous voulez mapper une URL vers un chemin complètement différent.
Pourquoi mon proxy_pass ne fonctionne pas ?
Vérifiez dans l'ordre :
- Le backend tourne-t-il ? →
curl http://127.0.0.1:3000 - Le slash final → avec ou sans
/change le comportement - SELinux (RHEL) →
setsebool -P httpd_can_network_connect 1 - Firewall → le port est-il ouvert ?
- Logs →
tail /var/log/nginx/error.log
Comment recharger Nginx sans interruption ?
# Tester d'abordsudo nginx -t
# Recharger (pas de downtime)sudo systemctl reload nginx
# OUsudo nginx -s reloadNe jamais utiliser restart en production si reload suffit.
Comment servir plusieurs sites sur le même serveur ?
Créez un fichier par site dans /etc/nginx/conf.d/ :
server { listen 80; server_name site1.com; root /var/www/site1;}
# /etc/nginx/conf.d/site2.confserver { listen 80; server_name site2.com; root /var/www/site2;}Nginx utilise le server_name pour router les requêtes.
Recettes Nginx
Section intitulée « Recettes Nginx »Chaque bloc ci-dessous se colle dans un fichier de conf.d/, en adaptant domaine et chemins. Un point de vigilance sur le contexte : les extraits qui ne montrent qu'un location s'insèrent dans un bloc server existant, tandis que les déclarations de zone comme proxy_cache_path et limit_req_zone se placent au niveau http, dans nginx.conf, sinon la configuration est rejetée au test.
Site statique
Section intitulée « Site statique »Le bloc est autonome : il déclare son écoute, son domaine et sa racine. try_files renvoie un 404 explicite quand ni le fichier ni le répertoire demandés n'existent.
server { listen 80; server_name example.com; root /var/www/html; index index.html; location / { try_files $uri $uri/ =404; }}SPA (React/Vue/Angular)
Section intitulée « SPA (React/Vue/Angular) »Le fallback vers /index.html remplace le =404 : toute route absente du disque est confiée au routeur JavaScript de l'application.
location / { root /var/www/app; try_files $uri $uri/ /index.html;}Reverse proxy
Section intitulée « Reverse proxy »Ces trois proxy_set_header sont le minimum pour que le backend journalise l'adresse réelle du client et non celle de Nginx.
location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}WebSocket
Section intitulée « WebSocket »proxy_http_version 1.1 est obligatoire : en HTTP/1.0, l'en-tête Upgrade n'est pas transmis et la connexion retombe en HTTP classique.
location /ws/ { proxy_pass http://backend:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";}Cache proxy
Section intitulée « Cache proxy »proxy_cache_path se déclare dans le contexte http, jamais dans un location. proxy_cache_valid fixe ensuite la durée de conservation par code de réponse.
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g;
location / { proxy_pass http://backend; proxy_cache my_cache; proxy_cache_valid 200 10m;}Rate limiting
Section intitulée « Rate limiting »burst=20 autorise une rafale de vingt requêtes au-delà du débit fixé, et nodelay les sert immédiatement au lieu de les étaler dans le temps.
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ { limit_req zone=api burst=20 nodelay; proxy_pass http://backend;}Basic Auth
Section intitulée « Basic Auth »Le fichier .htpasswd se génère avec la commande htpasswd ; il doit être lisible par le worker Nginx et rester hors de la racine web.
location /admin/ { auth_basic "Zone Admin"; auth_basic_user_file /etc/nginx/.htpasswd; proxy_pass http://backend;}IP allowlist
Section intitulée « IP allowlist »La première règle correspondante l'emporte : c'est le deny all final qui rend les allow effectifs.
location /admin/ { allow 192.168.1.0/24; allow 10.0.0.0/8; deny all; proxy_pass http://backend;}Redirection HTTPS
Section intitulée « Redirection HTTPS »La variable $request_uri contient déjà le chemin et la chaîne de requête, ce qui préserve l'URL complète lors du 301.
server { listen 80; server_name example.com; return 301 https://$host$request_uri;}Page maintenance
Section intitulée « Page maintenance »Créer ou supprimer le fichier bascule le site sans recharger Nginx. La directive error_page intercepte le 503 et sert la page statique locale à la place du backend.
location / { if (-f /var/www/maintenance.html) { return 503; } proxy_pass http://backend;}error_page 503 /maintenance.html;location = /maintenance.html { root /var/www;}Upload lourd
Section intitulée « Upload lourd »client_max_body_size vaut 1 Mo par défaut : au-delà, Nginx répond 413 sans même contacter le backend.
location /upload/ { client_max_body_size 100M; proxy_pass http://backend; proxy_read_timeout 300s;}Load balancing
Section intitulée « Load balancing »least_conn dirige la requête vers le backend ayant le moins de connexions actives, et le mot-clé backup réserve le troisième serveur aux périodes d'indisponibilité des deux autres.
upstream backend_pool { least_conn; server backend1:3000; server backend2:3000; server backend3:3000 backup;}
location / { proxy_pass http://backend_pool;}Pièges à éviter
Section intitulée « Pièges à éviter »Ces huit erreurs partagent une caractéristique : la configuration est syntaxiquement valide, nginx -t ne signale rien, et le problème n'apparaît qu'à l'exécution. Les deux plus coûteuses en production sont le slash final de proxy_pass, qui modifie l'URI transmise sans le moindre message, et la résolution DNS figée en environnement conteneurisé, qui ne se manifeste qu'après un redéploiement du backend.
| Erreur | Conséquence | Solution |
|---|---|---|
| Slash final dans proxy_pass | /api/users → backend reçoit /users au lieu de /api/users | Sans slash = URI conservée. Testez avec curl -v |
| Confondre root et alias | 404 car le chemin n'existe pas | alias /data/photos/; avec slash final |
| Ordre des locations | Une location préfixe capture avant la regex | = (exact) > ^~ (préfixe prio) > regex > préfixe |
| DNS statique en Docker/K8s | Nginx garde l'ancienne IP après redéploiement | resolver 127.0.0.11; set $backend "http://app:3000"; proxy_pass $backend; |
| SELinux bloque le proxy | 502 Bad Gateway sur RHEL/Rocky | setsebool -P httpd_can_network_connect 1 |
| Permissions fichiers | 403 Forbidden sur les fichiers statiques | chown -R www-data:www-data /var/www && chmod -R 755 /var/www |
| try_files sans fallback SPA | 404 sur les routes frontend | try_files $uri $uri/ /index.html; |
| Logs dans le mauvais fichier | Debug impossible | access_log /var/log/nginx/monsite-access.log; |
Aide-mémoire
Section intitulée « Aide-mémoire »Ces tableaux regroupent les directives par famille pour retrouver une syntaxe sans rouvrir la documentation. Un rappel qui évite bien des tests ratés : chaque directive n'est valide que dans certains contextes, http, server ou location, et Nginx refuse de démarrer si elle est placée ailleurs.
Structure
Section intitulée « Structure »Ces blocs s'imbriquent dans un ordre imposé : location n'existe qu'à l'intérieur d'un server, et upstream uniquement au niveau http.
| Directive | Description |
|---|---|
server { } | Bloc de configuration d'un site |
location /path { } | Bloc pour un chemin spécifique |
upstream name { } | Groupe de backends |
include /path/*.conf; | Inclure d'autres fichiers |
Le couple listen et server_name décide quel bloc traite la requête. Si aucun nom ne correspond, Nginx retient le premier bloc déclaré sur ce port.
| Directive | Description |
|---|---|
listen 80; | Écouter sur port 80 |
listen 443 ssl http2; | HTTPS + HTTP/2 |
server_name example.com; | Nom de domaine du site |
server_name _; | Catch-all (default server) |
Fichiers
Section intitulée « Fichiers »root ajoute l'URI au chemin indiqué, alias le remplace intégralement : c'est l'origine classique des 404 sur un fichier pourtant présent.
| Directive | Description |
|---|---|
root /var/www/html; | Racine des fichiers |
index index.html; | Fichier par défaut |
try_files $uri $uri/ =404; | Chercher fichier/dossier ou 404 |
alias /autre/chemin/; | Remplacer le chemin (avec slash) |
Ces directives se placent dans un location. Les proxy_set_header déclarés localement remplacent en bloc ceux hérités du niveau parent, ils doivent donc être répétés.
| Directive | Description |
|---|---|
proxy_pass http://backend:3000; | Transmettre au backend |
proxy_set_header Host $host; | Transmettre le Host original |
proxy_set_header X-Real-IP $remote_addr; | Transmettre l'IP client |
proxy_read_timeout 120s; | Timeout lecture backend |
fullchain.pem contient le certificat et la chaîne intermédiaire ; charger cert.pem seul provoque des erreurs de validation sur les clients qui ne complètent pas la chaîne.
| Directive | Description |
|---|---|
ssl_certificate /path/fullchain.pem; | Certificat + chaîne |
ssl_certificate_key /path/privkey.pem; | Clé privée |
ssl_protocols TLSv1.2 TLSv1.3; | Forcer TLS moderne |
add_header Strict-Transport-Security "max-age=31536000"; | HSTS |
nginx -s reload relit la configuration sans couper les connexions en cours et conserve l'ancienne version si la nouvelle est invalide, d'où l'intérêt du nginx -t préalable.
| Commande | Description |
|---|---|
nginx -t | Tester la configuration |
nginx -T | Dump config complète (includes résolus) |
nginx -s reload | Recharger sans interruption |
nginx -s stop | Arrêter immédiatement |
À retenir
Section intitulée « À retenir »Ces sept points recouvrent les réflexes qui évitent la majorité des interruptions de service sur un Nginx en production, du test de configuration au comportement des en-têtes transmis au backend.
nginx -t: toujours tester avant de rechargerserver {}+location {}: les 2 blocs fondamentaux- TLS 1.2/1.3 uniquement : jamais TLS 1.0/1.1
proxy_set_header: transmettre les infos client au backend- Slash dans
proxy_pass: avec ou sans change tout reload≠restart: reload = sans interruption- Logs :
/var/log/nginx/error.logest votre ami
Prochaines étapes
Section intitulée « Prochaines étapes »Nginx couvre le service de fichiers et le reverse proxy sur une machine donnée. HAProxy va plus loin sur la répartition de charge et le suivi de santé des backends, Caddy échange une part de contrôle contre une gestion automatique des certificats, et l'usage en conteneur impose ses propres contraintes de résolution DNS.
Ressources
Section intitulée « Ressources »Le générateur Mozilla est le raccourci le plus fiable pour une section TLS à jour : il produit les directives ssl_protocols et ssl_ciphers correspondant au niveau de compatibilité navigateur retenu. Attention en revanche à docs.nginx.com, qui documente la version commerciale NGINX Plus et décrit des directives absentes de la version open source.
- Documentation officielle : nginx.org/en/docs
- NGINX (F5) Docs : docs.nginx.com, version commerciale
- Mozilla SSL Config Generator : ssl-config.mozilla.org, générer une config TLS sécurisée
- GitHub : github.com/nginx/nginx
FAQ - Questions Fréquemment Posées
Section intitulée « FAQ - Questions Fréquemment Posées »Ces réponses courtes reprennent l'installation par distribution, la structure d'un server block, la mise en place du reverse proxy et du load balancing, l'activation de Let's Encrypt, ainsi que le diagnostic des erreurs 502 et 403.
Définition
Nginx (prononcé "engine-x") est un serveur web et reverse proxy haute performance créé par Igor Sysoev en 2004.Fonctions principales
| Fonction | Description |
|---|---|
| Serveur web | Sert fichiers statiques (HTML, CSS, JS, images) |
| Reverse proxy | Transmet les requêtes à des backends (Node.js, Python) |
| Load balancer | Répartit le trafic entre plusieurs serveurs |
| Cache | Stocke les réponses pour accélérer les requêtes |
Pourquoi Nginx ?
Son architecture asynchrone et événementielle résout le problème C10K (10 000 connexions simultanées) que les serveurs traditionnels géraient mal.Sites utilisant Nginx
Netflix, Dropbox, WordPress.com, Airbnb et des millions d'autres sites.Installation rapide (version des dépôts)
sudo apt update && sudo apt install nginx
sudo systemctl enable --now nginx
Installation version récente (dépôt officiel)
# Ajouter la clé et le dépôt
sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
# Installer
sudo apt update && sudo apt install nginx
sudo systemctl enable --now nginx
Vérification
systemctl status nginx # Doit être active (running)
nginx -v # Affiche la version
curl -I http://localhost # Doit retourner HTTP 200
Installation
# Ajouter le dépôt officiel Nginx
sudo tee /etc/yum.repos.d/nginx.repo << 'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/rhel/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
EOF
# Installer et démarrer
sudo dnf install nginx
sudo systemctl enable --now nginx
Configuration SELinux
Si vous utilisez Nginx comme reverse proxy :# Autoriser les connexions réseau sortantes
sudo setsebool -P httpd_can_network_connect 1
Vérification
systemctl status nginx
curl http://localhost
Structure de base
# /etc/nginx/conf.d/monsite.conf
server {
listen 80;
server_name monsite.com www.monsite.com;
root /var/www/monsite;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Directives principales
| Directive | Rôle |
|---|---|
listen |
Port d'écoute (80, 443, etc.) |
server_name |
Domaines acceptés |
root |
Dossier racine des fichiers |
location |
Règles pour une URL |
Déploiement
# Toujours tester AVANT de recharger
sudo nginx -t
sudo systemctl reload nginx
try_files expliqué
try_files $uri $uri/ =404; signifie :- Chercher le fichier demandé
- Sinon chercher un répertoire
- Sinon retourner 404
Configuration de base
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
# Headers essentiels
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;
}
}
Attention au slash final
# Sans slash : URI complète transmise
location /api/ {
proxy_pass http://backend:3000;
}
# /api/users → http://backend:3000/api/users
# Avec slash : URI réécrite
location /api/ {
proxy_pass http://backend:3000/;
}
# /api/users → http://backend:3000/users
Timeouts pour backends lents
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
Installation de Certbot
# Ubuntu/Debian
sudo apt install certbot python3-certbot-nginx
# RHEL/Rocky
sudo dnf install epel-release
sudo dnf install certbot python3-certbot-nginx
Obtenir le certificat
sudo certbot --nginx -d example.com -d www.example.com
Certbot modifie automatiquement votre configuration.Configuration générée
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
Renouvellement automatique
sudo certbot renew --dry-run # Tester
systemctl list-timers | grep certbot # Vérifier le timer
Comparaison
| Critère | Nginx | Apache |
|---|---|---|
| Architecture | Asynchrone événementielle | Processus/threads (MPM) |
| Connexions simultanées | Excellent (C10K+) | Bon avec event MPM |
| Contenu statique | Très rapide | Rapide |
| Configuration dynamique | Non (.htaccess inexistant) | Oui (.htaccess) |
| Modules | Compilés à la construction | Chargement dynamique |
| Reverse proxy | Natif, performant | Possible (mod_proxy) |
| Consommation RAM | Faible | Modérée à élevée |
Quand choisir Nginx ?
- Reverse proxy devant apps Node/Python/Go
- Sites à fort trafic
- Servir du contenu statique
- Load balancing
Quand choisir Apache ?
- Hébergement mutualisé (besoin de .htaccess)
- Applications PHP traditionnelles
- Besoin de modules dynamiques spécifiques
Configuration de base
upstream backend_pool {
server backend1.example.com:3000;
server backend2.example.com:3000;
server backend3.example.com:3000 backup; # Serveur de secours
}
server {
location / {
proxy_pass http://backend_pool;
proxy_set_header Host $host;
}
}
Algorithmes disponibles
| Algorithme | Usage |
|---|---|
| round-robin | Défaut, alternance simple |
| least_conn | Envoie au serveur le moins chargé |
| ip_hash | Même client → même serveur (sessions) |
| hash | Hash personnalisé (ex: URI) |
Exemple least_conn
upstream backend_pool {
least_conn;
server backend1:3000;
server backend2:3000 weight=2; # Reçoit 2× plus
}
Causes fréquentes
| Cause | Diagnostic |
|---|---|
| Backend down | curl http://127.0.0.1:3000 → Connection refused |
| Mauvaise IP/port | Vérifier proxy_pass |
| SELinux bloque | Logs + setsebool |
| Timeout | Backend trop lent |
Diagnostic pas à pas
# 1. Le backend tourne-t-il ?
curl -I http://127.0.0.1:3000
# Si "Connection refused" → le backend est down
# 2. Nginx peut-il joindre le backend ?
sudo -u www-data curl http://127.0.0.1:3000
# 3. Logs détaillés
sudo tail -f /var/log/nginx/error.log
# Chercher: "connect() failed (111: Connection refused)"
Solution SELinux (RHEL/Rocky)
sudo setsebool -P httpd_can_network_connect 1
Solution timeout
proxy_read_timeout 120s;
Causes fréquentes
| Cause | Solution |
|---|---|
| Permissions fichiers | chown -R www-data:www-data /var/www/ |
| Permissions dossiers | Dossiers en 755, fichiers en 644 |
| Index manquant | Ajouter index index.html; |
| autoindex off | Activer autoindex on; si voulu |
| deny all actif | Vérifier les règles allow/deny |
Diagnostic
# Vérifier les permissions
ls -la /var/www/monsite/
# Corriger les permissions
sudo chown -R www-data:www-data /var/www/monsite/
sudo find /var/www/monsite/ -type d -exec chmod 755 {} \;
sudo find /var/www/monsite/ -type f -exec chmod 644 {} \;
# Vérifier que index.html existe
ls -la /var/www/monsite/index.html
Logs
sudo tail -f /var/log/nginx/error.log
# Chercher: "directory index of \"/var/www/...\" is forbidden"
Commandes de validation
# Tester la syntaxe (OBLIGATOIRE avant reload)
sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# Afficher la config complète (includes résolus)
sudo nginx -T | less
En cas d'erreur
Nginx indique la ligne exacte du problème :nginx: [emerg] unknown directive "proxypass" in /etc/nginx/conf.d/api.conf:12
→ Corriger ligne 12 (proxypass → proxy_pass)Workflow sécurisé
# 1. Éditer la config
sudo nano /etc/nginx/conf.d/monsite.conf
# 2. Tester
sudo nginx -t
# 3. Recharger seulement si OK
sudo systemctl reload nginx
Règle d'or : Jamais de reload sans nginx -t réussi.Configuration
# Dans http {} : définir la zone
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/ {
# Appliquer la limite
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend:3000;
}
}
Paramètres expliqués
| Paramètre | Rôle |
|---|---|
$binary_remote_addr |
Limite par IP client |
zone=api_limit:10m |
Zone de 10MB en mémoire |
rate=10r/s |
10 requêtes par seconde max |
burst=20 |
20 requêtes en rafale autorisées |
nodelay |
Refuse immédiatement si dépassé |
Autres clés possibles
# Par URI
limit_req_zone $request_uri zone=uri_limit:10m rate=5r/s;
# Par serveur (global)
limit_req_zone $server_name zone=server_limit:10m rate=100r/s;