Nginx renvoie 502 : diagnostiquer l’upstream avant d’augmenter les timeouts
Un 502 Bad Gateway derrière Nginx ne signifie pas forcément que Nginx est en panne. Le plus souvent, le proxy a bien reçu la requête du client, mais il n’a pas obtenu une réponse exploitable de l’application située derrière lui : service arrêté, mauvais port, socket inaccessible, connexion TLS incorrecte ou réponse interrompue.
Le mauvais réflexe consiste à augmenter tous les délais ou à redémarrer l’ensemble de la pile. On perd alors l’indice le plus utile et on risque de transformer une panne reproductible en incident intermittent. Une méthode propre commence par identifier l’upstream exact, lire l’erreur Nginx, puis tester ce même upstream depuis le même environnement réseau.
Comprendre le chemin réel de la requête
Dans une configuration classique, le trajet ressemble à ceci :
Client → Nginx → application upstream → base de données ou service externe
Le code 502 apparaît entre Nginx et l’upstream. Cela ne garantit pas que les dépendances suivantes fonctionnent : une application peut accepter la connexion puis fermer brutalement parce que sa base de données est indisponible. Il faut donc diagnostiquer couche par couche.
Commencez par localiser la configuration effectivement chargée :
sudo nginx -T 2>/dev/null | less
Recherchez le bloc server, la directive location concernée et surtout proxy_pass. Attention avant de partager cette sortie : une configuration complète peut contenir des noms internes, des chemins, des en-têtes ou des secrets ajoutés par erreur dans le fichier.
Étape 1 : lire le journal d’erreurs avant de modifier quoi que ce soit
Le navigateur ne montre que « 502 ». Le journal Nginx donne généralement la cause technique.
sudo journalctl -u nginx --since "15 min ago"
sudo tail -n 100 /var/log/nginx/error.log
Le chemin dépend de la distribution et de la directive error_log. Utilisez la sortie de nginx -T pour confirmer le fichier réellement configuré.
| Message observé | Piste principale |
|---|---|
connect() failed (111: Connection refused) | Aucun service n’écoute à l’adresse ou au port indiqué |
connect() to unix:... failed (13: Permission denied) | Nginx ne peut pas traverser un répertoire ou ouvrir le socket |
upstream timed out | L’upstream n’a rien envoyé dans le délai prévu |
upstream prematurely closed connection | L’application a fermé la connexion avant une réponse complète |
SSL_do_handshake() failed | Problème TLS, SNI, certificat ou protocole vers l’upstream |
Étape 2 : vérifier que l’application écoute vraiment
Si proxy_pass vise http://127.0.0.1:3000, vérifiez d’abord qu’un processus écoute bien sur ce port :
sudo ss -ltnp | grep ':3000'
systemctl status mon-application
journalctl -u mon-application --since "15 min ago"
Un service peut être déclaré « actif » tout en étant incapable de traiter une requête. Testez directement son endpoint, sans passer par Nginx :
curl -v --max-time 10 http://127.0.0.1:3000/
curl -v --max-time 10 http://127.0.0.1:3000/health
L’endpoint /health n’existe pas partout : utilisez seulement celui réellement prévu par l’application. Si l’accès direct échoue aussi, le problème n’est pas dans la couche proxy. Il faut lire les journaux de l’application et contrôler ses dépendances.
Attention au piège de localhost dans Docker
Dans un conteneur Nginx, 127.0.0.1 désigne le conteneur Nginx lui-même, pas le serveur hôte ni le conteneur applicatif. Une configuration qui fonctionne hors Docker peut donc produire immédiatement un refus de connexion une fois conteneurisée.
Vérifiez les réseaux et les noms de services :
docker compose ps
docker network inspect <nom_du_reseau>
docker compose logs --tail=100 nginx
docker compose logs --tail=100 application
Le test doit être effectué depuis le même espace réseau que Nginx. Si l’image contient un client HTTP :
docker compose exec nginx curl -v http://application:3000/
N’installez pas des outils de diagnostic dans un conteneur de production minimal juste pour ce test. Utilisez plutôt un conteneur temporaire raccordé au même réseau ou les outils déjà prévus dans votre procédure d’exploitation.
Étape 3 : diagnostiquer un socket Unix sans faire chmod 777
PHP-FPM, Gunicorn et d’autres services peuvent communiquer avec Nginx par un socket Unix. Dans ce cas, le fichier du socket peut avoir les bons droits alors qu’un répertoire parent empêche le processus Nginx de le traverser.
sudo stat /run/mon-app/app.sock
sudo namei -l /run/mon-app/app.sock
ps -eo user,group,comm | grep nginx
namei -l affiche les permissions de chaque composant du chemin. Corrigez ensuite l’utilisateur, le groupe ou la création du socket dans la configuration du service qui le possède. Ne passez pas le socket et ses répertoires en 777 : cela masque le problème et élargit inutilement les accès.
Sur un système utilisant SELinux ou AppArmor, des permissions Unix correctes ne suffisent pas toujours. Cherchez alors un refus dans les journaux de sécurité au lieu de désactiver globalement le mécanisme de protection.
Étape 4 : distinguer connexion lente et réponse lente
Nginx sépare plusieurs délais. Les deux plus souvent confondus sont :
proxy_connect_timeout: délai pour établir la connexion avec l’upstream ;proxy_read_timeout: délai entre deux opérations de lecture successives de la réponse.
Le second n’est pas une durée maximale pour toute la requête. Si l’upstream n’envoie rien pendant cette période, Nginx ferme la connexion.
Avant d’augmenter une valeur, mesurez le comportement direct :
curl -sS -o /dev/null -w 'connect=%{time_connect} starttransfer=%{time_starttransfer} total=%{time_total}\n' http://127.0.0.1:3000/route-lente
Si l’application met régulièrement une minute avant le premier octet, un timeout plus long peut être cohérent pour une opération légitime. Mais si elle devrait répondre en 200 ms, augmenter la limite ne corrige rien : cela garde seulement les connexions bloquées plus longtemps. Cherchez la requête lente, la saturation du pool, le verrou ou la dépendance distante.
Étape 5 : tester le TLS vers un upstream HTTPS
Avec un proxy_pass https://..., Nginx doit aussi réussir la négociation TLS. Testez le serveur en envoyant le bon nom SNI :
openssl s_client -connect upstream.example:443 -servername upstream.example -verify_return_error
La directive proxy_ssl_server_name contrôle l’envoi du nom de serveur via SNI et elle est désactivée par défaut dans la documentation Nginx. Si l’upstream héberge plusieurs certificats sur la même adresse, l’absence de SNI peut présenter le mauvais certificat ou faire échouer la négociation.
Si vous activez la vérification du certificat de l’upstream, configurez également une autorité de confiance correcte. Désactiver la vérification « pour que ça marche » supprime une protection importante contre l’interception et les erreurs de destination.
Modifier proprement, puis recharger sans coupure
Une fois la cause confirmée, effectuez le changement minimal : corriger le port, démarrer le bon service, réparer les droits du socket ou ajuster un délai justifié. Testez toujours la syntaxe avant le rechargement :
sudo nginx -t
sudo systemctl reload nginx
Lors d’un rechargement, le processus maître vérifie la nouvelle configuration. Si elle ne peut pas être appliquée, Nginx conserve l’ancienne. Un redémarrage brutal n’est donc pas nécessaire pour chaque correction et peut provoquer une interruption évitable.
La séquence de diagnostic à retenir
- Identifier le
proxy_passréellement chargé. - Lire le journal d’erreurs Nginx au moment exact du 502.
- Tester l’upstream directement depuis le même environnement réseau.
- Contrôler le service, son port ou les permissions de son socket.
- Mesurer la latence avant de toucher aux timeouts.
- Vérifier SNI et certificat si l’upstream utilise HTTPS.
- Valider avec
nginx -t, puis recharger proprement.
Un 502 n’est pas une cause, c’est le symptôme d’un échange raté. Le journal indique généralement quelle étape casse ; le test direct confirme si l’application répond ; le contexte réseau explique les cas Docker ; les permissions tranchent pour un socket Unix. En suivant cet ordre, on évite les redémarrages à l’aveugle et les timeouts gonflés qui repoussent simplement la prochaine panne.
