Nginx est un reverse proxy et un serveur web rapides et flexibles, mais les problèmes en production peuvent être délicats lorsque les symptômes se chevauchent. Ce guide vous donne un flux de travail de dépannage pratique avec des commandes sûres, des astuces de lecture des logs et des mini runbooks pour les pannes courantes. Vous serez en mesure de :

- Identifier ce qui a cassé et où : Nginx, le réseau ou l'application upstream.

- Lire et filtrer les logs d'accès et d'erreur pour voir la cause et l'effet.

- Valider la configuration sans risque avant de recharger.

- Corriger les erreurs courantes comme 502, 504, 413, 403, 404 et les échecs de handshake TLS.

- Appliquer la même approche sur hôtes Linux, dans Docker et avec Kubernetes Ingress.

Des exemples concrets sont inclus pour vous permettre de pratiquer localement et de réduire le temps d'indisponibilité quand c'est critique.

## Vue d'ensemble du flux de travail

Utilisez cette boucle en production et gardez vos étapes petites. Chaque étape inclut des commandes pratiques et des astuces.

- Confirmer le symptôme

- Capturer l'erreur exacte et le chemin d'URL. Exemple : 502 sur GET /api .

- Reproduire avec curl pour éviter le bruit côté client :

curl -iv http://example.com/api
 curl -kiv https://example.com/secure 

- Vérifier le processus et les ports

- Nginx tourne-t-il ?

 systemctl status nginx
 # ou
 service nginx status 

- Écoute-t-il là où vous l'attendez ?

 ss -tulpn | grep -E ':80|:443' 

- Qu'est-ce qui utilise le port si ce n'est pas Nginx ?

 fuser -v 80/tcp 443/tcp
 lsof -i :80 -i :443 
 Emplacements par défaut :

- Inspecter les logs rapidement

Suivre et filtrer les erreurs récentes :

- /var/log/nginx/error.log

- /var/log/nginx/access.log

tail -Fn200 /var/log/nginx/error.log
 grep -iE 'error|crit|alert' /var/log/nginx/error.log
 tail -Fn200 /var/log/nginx/access.log
 awk '{print $1, $4, $6, $7, $9, $10}' /var/log/nginx/access.log 
 Astuce : augmenter temporairement le détail du log d'erreur en environnement de test :

error_log /var/log/nginx/error.log info;
 # ou debug si compilé avec debug 
 Toujours tester avant de recharger :

- Valider la configuration sans risque

grep -RIn 'error|warn' /etc/nginx/*.conf /etc/nginx/conf.d || true
 nginx -t 
 Sauvegarder avant les changements :

cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.$(date +%F-%H%M) 
 Si vous utilisez proxy_pass http://app:3000 , testez la cible directement depuis l'hôte Nginx :

- Sonner l'upstream et le réseau

curl -iv http://127.0.0.1:3000/health || curl -iv http://app:3000/health 
 Dans Docker, assurez-vous que le nom résout et que le port est accessible depuis le conteneur Nginx :

docker exec -it nginx sh -lc 'getent hosts app && curl -sS http://app:3000/health' 
 Afficher la chaîne de certificats et la gestion SNI :

- Vérifications TLS de bon sens

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates 
 Tester à nouveau, puis recharger sans couper les connexions :

- Appliquer les corrections ciblées via les mini runbooks (ci-dessous)

- Recharger sans risque et de façon atomique

nginx -t && systemctl reload nginx || service nginx reload 

- Vérifier et surveiller les régressions

 curl -Is http://example.com/ | head -n1
 curl -Is https://example.com/ | head -n1
 for p in / /health /api/version; do curl -sSfo /dev/null -w '%{http_code} %{url_effective}\n' http://example.com"$p\); done 

- Durcir pour éviter la récurrence

- Ajouter des endpoints de santé sur les upstreams.

- Ajuster timeouts et buffers selon le trafic observé.

- Centraliser les logs ; ajouter des alertes sur les pics de 5xx et 499.

## Mini runbooks : Erreurs Nginx courantes et correctifs

 Chaque sous-section donne les symptômes, les vérifications de cause racine et les correctifs sûrs.

### 502 Bad Gateway (proxy vers l'app)

Symptômes : 502 sur les routes proxifiées. Le log d'erreur peut montrer upstream connect ou prematurely closed connection .

Vérifications :

# L'upstream est-il up ?
curl -iv http://127.0.0.1:3000/health

# Résolution DNS si un nom est utilisé dans proxy_pass
getent hosts app || dig +short app

# Exemple socket ou PHP-FPM
ls -l /run/php/php-fpm.sock && ss -xl | grep php-fpm 
 Pièges de config et correctifs :

location /api/ {
 proxy_pass http://127.0.0.1:3000/; # noter le comportement du slash final
 proxy_set_header Host $host;
 proxy_set_header X-Forwarded-For $remote_addr;
 proxy_set_header X-Forwarded-Proto $scheme;
 proxy_connect_timeout 5s;
 proxy_read_timeout 60s;
} 

- Si le DNS Docker est utilisé, ajouter un resolver dans http{} quand on proxy vers des noms qui changent :

 resolver 127.0.0.11 valid=30s; # DNS embarqué Docker 

- Pour PHP-FPM :

 location ~ \.php$ {
 fastcgi_pass unix:/run/php/php-fpm.sock; # ou 127.0.0.1:9000
 include fastcgi_params;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 } 

### 504 Gateway Timeout

 Symptômes : Attentes longues se terminant en 504.

Vérifications et correctifs :

# Augmenter les timeouts pour correspondre au comportement upstream
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

# Garder l'upstream en vie pour réduire la surcharge de handshake
upstream app {
 server 127.0.0.1:3000;
 keepalive 16;
} 
 Revoir aussi les logs upstream pour requêtes lentes ou pauses GC.

### 413 Request Entity Too Large

Symptômes : Les uploads échouent ; le log d'erreur montre client intended to send too large body .

Correctif :

# Placer dans http, server ou location selon le besoin
client_max_body_size 50m; 
 Recharger et retester uniquement le chemin d'upload.

### 404 Not Found et 403 Forbidden

Causes fréquentes du 404 :

- Mauvais mapping root vs alias .

- try_files mal ordonné.

- Surprises de priorité des locations.

Causes fréquentes du 403 :

- Fichier index manquant.

- Permissions fichiers ou contexte SELinux.

Patterns de correction :

server {
 root /var/www/site/public;
 index index.html index.htm;

 location /images/ {
 alias /mnt/media/img/; # alias remplace root pour ce bloc
 autoindex off;
 }

 location / {
 try_files $uri $uri/ /index.html;
 }
} 
 Permissions Linux et SELinux :

# S'assurer que l'utilisateur nginx peut lire les répertoires
namei -l /var/www/site/public/index.html

# Réparation contexte SELinux (sur distros avec SELinux)
sudo restorecon -R /var/www/site || sudo chcon -R -t httpd_sys_content_t /var/www/site 

### Échecs de handshake TLS

 Symptômes : curl -k fonctionne mais les navigateurs échouent ; le log d'erreur montre des erreurs SSL ou pas de cipher partagé.

Patterns de correction :

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;

 add_header Strict-Transport-Security 'max-age=31536000' always;
} 
 Vérifier la chaîne et le SNI :

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null | openssl x509 -noout -subject -issuer -dates 
 Si vous voyez le mauvais certificat, vérifier le matching server_name et qu'un seul default_server gère le 443.

### Problèmes d'en-têtes 400/431 et 499 Client Closed Request

- En-têtes volumineux : augmenter les buffers.

large_client_header_buffers 4 16k;
 underscores_in_headers on; # si vos clients envoient des underscores 

- 499 indique que le client a fermé la connexion prématurément. Vérifier la latence upstream et les timeouts client ; réduire le temps de réponse, garder les corps petits.

### Problèmes WebSocket et HTTP Upgrade

 map $http_upgrade $connection_upgrade {
 default upgrade;
 '' close;
}

location /ws/ {
 proxy_pass http://127.0.0.1:8080/;
 proxy_http_version 1.1;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection $connection_upgrade;
} 

### Échecs de rechargement ou de démarrage

 Symptômes : nginx -t échoue ou le rechargement ne fait rien.

Vérifications :

nginx -t
journalctl -u nginx -n 100 --no-pager
# Erreurs courantes : listen dupliqué, directive inconnue, mauvais include 
 Conflits de ports :

ss -tulpn | grep ':80\|:443' || true
# Arrêter l'autre processus ou changer les ports d'écoute Nginx 

## Spécificités Docker, Kubernetes Ingress et Linux

### Docker

- Monter la config à /etc/nginx et tester à l'intérieur du conteneur :

 docker exec -it nginx sh -lc 'nginx -t && nginx -V && ss -tulpn' 

- S'assurer que les noms dans proxy_pass résolvent à l'intérieur du conteneur. Envisager d'ajouter :

 resolver 127.0.0.11 valid=30s; 

### Kubernetes Ingress (NGINX Controller)

- Inspector les logs et événements du contrôleur :

 kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=200
 kubectl describe ingress your-ingress -n your-namespace 

- Paramètres courants : timeouts (annotations), taille du corps, en-têtes proxy. S'assurer que votre Service pointe vers des Pods sains et que les readinessProbes réussissent.

### Bases Linux

- Pare-feu : autoriser 80 et 443 (ufw, firewalld, iptables).

- SELinux : vérifier les contextes pour le contenu et les certificats.

- Systemd : privilégier reload plutôt que restart pour les changements de config.

## Plan pilote local

 Commencez avec un petit pilote que vous pouvez inspecter localement. Gardez une portée étroite et mesurable.

### Objectifs

- Servir du contenu statique à / et proxy /api vers une app démo sur 127.0.0.1:9000 .

- Exercer les cas 502, 413 et TLS à la demande.

- Pratiquer les rechargements sûrs avec zéro requête échouée pendant le changement.

### Installation

- Upstream démo

# App démo simple (exemple Python)
 python3 -m http.server 9000 --bind 127.0.0.1 & 

- Config Nginx

 # /etc/nginx/conf.d/pilot.conf
 server {
 listen 80;
 server_name localhost;
 root /usr/share/nginx/html;
 index index.html;

 location /api/ {
 proxy_pass http://127.0.0.1:9000/;
 proxy_set_header Host $host;
 proxy_set_header X-Forwarded-For $remote_addr;
 proxy_set_header X-Forwarded-Proto $scheme;
 client_max_body_size 5m; # ajuster dans les tests
 }
 } 

- Valider et recharger

 nginx -t && systemctl reload nginx || service nginx reload 

### Cas de test

- Chemin nominal :

 curl -sS -w '%{http_code}\n' -o /dev/null http://localhost/
 curl -sS -w '%{http_code}\n' -o /dev/null http://localhost/api/ 

- Drill 502 : arrêter l'upstream et confirmer le comportement Nginx, puis restaurer.

 pkill -f 'http.server 9000' || true
 curl -sS -v http://localhost/api/
 python3 -m http.server 9000 --bind 127.0.0.1 & 

- Drill 413 : uploader un fichier plus gros que client_max_body_size et surveiller error.log.

 head -c 10M </dev/urandom > big.bin
 curl -sS -F file=@big.bin http://localhost/api/upload 

- Drill TLS (optionnel) : ajouter un certificat auto-signé et un server 443, puis vérifier avec openssl.

### Critères de succès

- Réponses 200 pour / et /api avec l'upstream up.

- 502 apparaît quand l'upstream est down ; disparaît quand il revient.

- 413 survient pour corps trop gros ; succès après augmentation de client_max_body_size .

- Aucune erreur dans nginx -t et rechargements propres.

- error.log montre clairement chaque cas de test.

### Rollback

 cp -a /etc/nginx/nginx.conf.bak.* /etc/nginx/nginx.conf 2>/dev/null || true
# Ou déplacer pilot.conf et recharger
mv /etc/nginx/conf.d/pilot.conf /etc/nginx/conf.d/pilot.conf.off
nginx -t && systemctl reload nginx || service nginx reload 

## Conclusion

 Utilisez la boucle : confirmer le symptôme, vérifier processus et ports, lire les logs, valider la config, tester l'upstream et le TLS, appliquer un correctif ciblé, recharger sans risque, et vérifier. Pratiquez avec le pilote local jusqu'à ce que chaque mini runbook devienne routinier. Appliquez ensuite les mêmes patterns à Docker et Kubernetes Ingress : vérifier la résolution de noms, les endpoints de santé, les timeouts et les en-têtes. Gardez les changements petits, fiez-vous aux logs et à curl pour un retour rapide, et documentez les commandes exactes qui ont marché pour que votre équipe puisse les reproduire en confiance.