Comment diagnostiquer et résoudre les erreurs Nginx 502 Bad Gateway

Résolvez les erreurs Nginx 502 en vérifiant les journaux d'erreurs, l'état des serveurs en amont, les permissions des sockets, les paramètres de proxy, les délais d'attente et les pare-feux.

Comment diagnostiquer et résoudre les erreurs Nginx 502 Bad Gateway

Nginx est un serveur web et proxy inverse puissant et populaire, souvent utilisé pour servir du contenu statique, équilibrer le trafic et transmettre des requêtes à divers serveurs d'application en amont comme PHP-FPM, Node.js, Python Gunicorn ou Apache Tomcat. Lorsque Nginx rencontre un problème de communication avec l'un de ces serveurs en amont, il répond généralement par une erreur "502 Bad Gateway".

Commencez par le journal d'erreurs Nginx, puis vérifiez si le processus en amont est en cours d'exécution, accessible et autorisé à répondre.

Comprendre l'erreur Nginx 502 Bad Gateway

Une erreur 502 Bad Gateway indique que Nginx, agissant comme proxy inverse, a reçu une réponse invalide d'un serveur en amont. Cela signifie que Nginx s'est connecté avec succès à un serveur en amont mais n'a reçu aucune réponse, une réponse incomplète ou une réponse qu'il n'a pas pu comprendre. Essentiellement, le problème ne vient pas de Nginx lui-même, mais du service avec lequel Nginx tente de communiquer.

Les serveurs en amont courants incluent :

  • PHP-FPM : Pour les applications PHP (ex : WordPress, Laravel).
  • Gunicorn/uWSGI : Pour les applications Python (ex : Django, Flask).
  • Node.js : Pour les applications JavaScript.
  • Apache Tomcat : Pour les applications Java.
  • Autres serveurs web : Comme le serveur HTTP Apache servant du contenu spécifique.

L'erreur 502 est un indicateur crucial que le backend de votre application ne fonctionne pas correctement ou est inaccessible pour Nginx.

Diagnostic étape par étape

La clé pour résoudre une erreur 502 est un diagnostic systématique. Commencez par les causes les plus probables et investiguez progressivement.

1. Vérifiez d'abord les journaux d'erreurs Nginx

Vos journaux d'erreurs Nginx sont la principale source d'informations. Ils contiennent souvent des détails spécifiques sur la raison pour laquelle Nginx n'a pas pu communiquer avec le serveur en amont.

  • Emplacement : Généralement situé dans /var/log/nginx/error.log.
  • Commande : Utilisez tail -f pour surveiller les journaux en temps réel tout en essayant de reproduire l'erreur.
tail -f /var/log/nginx/error.log

Ce qu'il faut rechercher :

  • connect() failed (111: Connection refused) : Indique que le serveur en amont n'écoute pas sur l'adresse/le port spécifié ou qu'un pare-feu bloque la connexion.
  • upstream timed out : Le serveur en amont a mis trop de temps à répondre.
  • upstream prematurely closed connection : Le serveur en amont a fermé la connexion avant d'envoyer une réponse complète.
  • no live upstreams while connecting to upstream : Nginx n'a trouvé aucun serveur en amont disponible configuré.

2. Vérifiez l'état du serveur en amont

Une fois que vous avez des indices provenant des journaux d'erreurs Nginx, vérifiez l'état de votre serveur d'application en amont.

  • Pour PHP-FPM :

    sudo systemctl status php8.2-fpm
    
  • Pour Node.js/Python/Autres applications personnalisées : Vérifiez si le processus est en cours d'exécution.

    ps aux | grep node
    ps aux | grep gunicorn
    

    Si vous utilisez un gestionnaire de processus comme PM2 (Node.js) ou Supervisor (général), vérifiez son état.

    pm2 status
    sudo supervisorctl status
    

Si le service ne fonctionne pas, essayez de le démarrer et vérifiez ses propres journaux pour les erreurs.

sudo systemctl start php8.2-fpm

3. Vérifiez la connectivité réseau vers l'amont

Assurez-vous que Nginx peut atteindre le serveur en amont sur le port ou le chemin de socket configuré.

  • Pour les connexions TCP/IP (ex : 127.0.0.1:8000) : Utilisez telnet ou nc (netcat) pour tester la connectivité du port depuis le serveur Nginx.

    telnet 127.0.0.1 8000
    nc -vz 127.0.0.1 8000
    

    Une connexion réussie devrait afficher Connected to 127.0.0.1. ou succeeded!. Si elle se bloque ou affiche Connection refused, le service en amont n'écoute pas ou un pare-feu le bloque.

  • Pour les sockets Unix (ex : unix:/run/php/phpX.X-fpm.sock) : Vérifiez que le fichier socket existe et a les bonnes permissions.

    ls -l /run/php/phpX.X-fpm.sock
    

    Nginx doit avoir les permissions de lecture/écriture sur ce fichier socket. L'utilisateur Nginx (ex : www-data) doit faire partie du groupe propriétaire du socket (ex : www-data ou php-fpm).

Causes courantes et solutions

En fonction de vos étapes de diagnostic, voici les causes les plus fréquentes des erreurs 502 et comment les résoudre.

1. Serveur en amont non démarré ou planté

Cause : L'application vers laquelle Nginx tente de faire le proxy (ex : PHP-FPM, Gunicorn, application Node.js) n'est pas en cours d'exécution ou a planté.

Solution : Démarrez ou redémarrez le service en amont.

# Exemple pour PHP-FPM
sudo systemctl start php8.2-fpm
# S'il est déjà en cours d'exécution et que vous soupçonnez un plantage, redémarrez-le :
sudo systemctl restart php8.2-fpm

# Pour les applications personnalisées, utilisez leurs commandes de démarrage/redémarrage spécifiques

Astuce : Assurez-vous que vos services en amont sont configurés pour démarrer automatiquement au démarrage du système. Pour les services systemd, utilisez systemctl enable phpX.X-fpm.

2. Surcharge du serveur en amont / Épuisement des ressources

Cause : Le serveur en amont est submergé, manque de mémoire, de CPU ou atteint les limites de processus, ce qui l'empêche de répondre ou de refuser de nouvelles connexions.

Symptômes : Les journaux d'erreurs Nginx peuvent montrer connection refused ou upstream timed out par intermittence, surtout sous charge. Les outils de surveillance système (top, htop, free -h) montrent une utilisation élevée des ressources.

Solutions :

  • Pour PHP-FPM : Ajustez les paramètres du pool PHP-FPM dans son fichier de configuration (ex : /etc/php/X.X/fpm/pool.d/www.conf).

    • pm.max_children : Le nombre maximum d'enfants pouvant être actifs en même temps.
    • pm.start_servers : Le nombre d'enfants créés au démarrage.
    • pm.min_spare_servers, pm.max_spare_servers : Contrôle du nombre d'enfants inactifs conservés.
    ; Exemple pour la gestion dynamique des processus
    pm = dynamic
    pm.max_children = 50
    pm.start_servers = 10
    pm.min_spare_servers = 5
    pm.max_spare_servers = 20
    
    • Augmentez memory_limit dans php.ini si les scripts épuisent la mémoire.
  • Pour d'autres applications : Augmentez le nombre de processus de travail, de threads ou allouez plus de mémoire si possible. Surveillez les métriques spécifiques de votre application.

  • Délais d'attente Nginx : Augmentez les directives proxy_connect_timeout, proxy_send_timeout et proxy_read_timeout de Nginx dans votre configuration Nginx, mais comprenez que cela retarde simplement l'erreur si le backend est vraiment en difficulté.

    http {
        ...
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        ...
    }
    

3. Configuration en amont incorrecte dans Nginx

Cause : Nginx est configuré pour se connecter à la mauvaise adresse IP, au mauvais port ou au mauvais chemin de socket Unix pour le serveur en amont.

Symptômes : Les journaux d'erreurs Nginx montrent connect() failed (111: Connection refused) immédiatement après une requête.

Solution : Examinez attentivement la configuration de votre bloc serveur Nginx (/etc/nginx/sites-available/your_site.conf).

  • Pour les amonts HTTP/HTTPS :

    location /app {
        proxy_pass http://127.0.0.1:8000; # Assurez-vous que l'IP et le port sont corrects
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
  • Pour PHP-FPM via socket Unix :

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/phpX.X-fpm.sock; # Vérifiez que ce chemin correspond exactement à la configuration PHP-FPM
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    
  • Pour PHP-FPM via TCP/IP :

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000; # Vérifiez l'IP et le port
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    

Après avoir effectué des modifications, testez toujours votre configuration Nginx et rechargez/redémarrez Nginx :

nginx -t
systemctl reload nginx # Ou redémarrez si -t indique un besoin

4. Dépassement du request_terminate_timeout de PHP-FPM

Cause : Un script PHP prend plus de temps à s'exécuter que le paramètre request_terminate_timeout dans PHP-FPM. Nginx attend la réponse, mais PHP-FPM termine le script, ce qui fait que Nginx reçoit une réponse incomplète.

Symptômes : Les journaux d'erreurs Nginx peuvent montrer upstream timed out ou script timed out. Les journaux PHP-FPM peuvent montrer child XX exited on signal 9 (SIGKILL).

Solution :

  • Augmentez request_terminate_timeout : Dans votre configuration de pool PHP-FPM (www.conf), trouvez et ajustez cette directive. La définir sur 0 désactive le délai d'attente, mais cela n'est généralement pas recommandé car les scripts de longue durée peuvent bloquer les ressources.

    request_terminate_timeout = 300 # Augmentez à 5 minutes (300 secondes)
    
  • Augmentez fastcgi_read_timeout dans Nginx : Ce délai d'attente Nginx doit être égal ou supérieur à request_terminate_timeout.

    location ~ \.php$ {
        ...
        fastcgi_read_timeout 300s; # Doit être >= request_terminate_timeout de PHP-FPM
        ...
    }
    

Avertissement : Bien que l'augmentation des délais d'attente puisse résoudre l'erreur 502, elle peut masquer des problèmes de performance sous-jacents. La meilleure solution à long terme est d'optimiser le script PHP lent.

5. Problèmes de pare-feu

Cause : Un pare-feu (soit sur le serveur Nginx, soit sur le serveur en amont s'ils sont séparés) bloque les connexions vers le port ou le socket en amont.

Solution :

  • Vérifiez l'état du pare-feu :

    sudo ufw status # Pour UFW (Ubuntu/Debian)
    sudo firewall-cmd --list-all # Pour firewalld (CentOS/RHEL)
    sudo iptables -L # Pour iptables
    
  • Ouvrez les ports nécessaires : Assurez-vous que le port utilisé par Nginx pour se connecter à l'amont (ex : 9000 pour PHP-FPM via TCP/IP) est ouvert.

    sudo ufw allow from 127.0.0.1 to any port 9000 # Autoriser localhost à se connecter au port 9000
    sudo firewall-cmd --permanent --add-port=9000/tcp # Pour firewalld
    sudo firewall-cmd --reload
    
  • Désactivez temporairement le pare-feu à des fins de test uniquement dans un environnement contrôlé, puis réactivez-le et configurez-le correctement.

6. Interférence de SELinux ou AppArmor

Cause : Les améliorations de sécurité comme SELinux (sur RHEL/CentOS) ou AppArmor (sur Ubuntu/Debian) peuvent empêcher Nginx d'accéder au socket en amont ou d'effectuer des connexions réseau, même si les permissions de fichiers et les pare-feux sont correctement configurés.

Symptômes : Les journaux peuvent montrer permission denied ou des messages similaires, en particulier dans /var/log/audit/audit.log (pour SELinux).

Solution :

  • Vérifiez audit.log :

    sudo grep nginx /var/log/audit/audit.log
    
  • Définissez temporairement SELinux en mode permissif : sudo setenforce 0. Si l'erreur est résolue, SELinux est le coupable. Vous devrez ensuite générer et appliquer des politiques SELinux appropriées (ex : audit2allow). N'oubliez pas de le remettre en mode enforcing (sudo setenforce 1).

  • Vérifiez l'état d'AppArmor : sudo aa-status. Si AppArmor est actif, vous devrez peut-être ajuster le profil Nginx.

7. Corps de requête/réponse volumineux (Mise en mémoire tampon du proxy)

Cause : Les paramètres de mise en mémoire tampon par défaut du proxy Nginx peuvent être trop petits pour des corps de requête ou de réponse très volumineux, entraînant une fermeture prématurée de la connexion.

Symptômes : Les journaux d'erreurs Nginx peuvent montrer upstream prematurely closed connection while reading response header from upstream ou upstream prematurely closed connection while reading response body from upstream.

Solution : Ajustez les directives de mise en mémoire tampon du proxy Nginx dans votre bloc http, server ou location.

http {
    ...
    proxy_buffer_size   128k; # Taille du tampon pour la première partie de la réponse
    proxy_buffers   4 256k; # Nombre et taille des tampons pour le reste de la réponse
    proxy_busy_buffers_size   256k; # Taille maximale des tampons occupés
    proxy_temp_file_write_size 256k; # Taille pour l'écriture dans des fichiers temporaires si la mise en mémoire tampon déborde
    ...
}

Remarque : Ces paramètres consomment plus de mémoire. Ajustez-les avec prudence en fonction des ressources de votre serveur et de la taille typique des réponses de votre application.

Conseils de dépannage généraux

  • Examinez tous les journaux pertinents : Outre les journaux d'erreurs Nginx, vérifiez également les journaux d'accès Nginx, les journaux d'application en amont (journaux PHP-FPM, Gunicorn, application Node.js) et les journaux système (/var/log/syslog, dmesg).
  • Redémarrez Nginx : Après toute modification de configuration, redémarrez toujours Nginx pour vous assurer qu'elles prennent effet : systemctl restart nginx.
  • Testez la configuration Nginx : Avant de redémarrer, validez la syntaxe de votre configuration Nginx : nginx -t.
  • Isolez le problème : Essayez de contourner Nginx et d'accéder directement à l'application en amont. Par exemple, si votre application Node.js est sur localhost:3000, utilisez curl http://localhost:3000 depuis la ligne de commande du serveur. Si cela échoue également, le problème vient définitivement de votre application, pas de Nginx.
  • Vérifiez l'espace disque : Un disque plein peut empêcher les applications d'écrire des fichiers temporaires ou des journaux, entraînant des plantages ou des échecs. Utilisez df -h pour vérifier l'utilisation du disque.

À retenir

Commencez par /var/log/nginx/error.log, puis vérifiez que l'amont est en cours d'exécution et accessible depuis l'hôte Nginx. Une fois que vous savez si l'échec est un refus de connexion, un délai d'attente, un refus d'autorisation ou une fermeture prématurée, la correction se trouve généralement dans le service en amont, les permissions du socket, les paramètres de délai d'attente ou la règle de pare-feu.