Credit : Logo officiel
Resoudre les erreurs 502 Bad Gateway Nginx
502 Bad Gateway : la page blanche qui fait sonner votre telephone
Vendredi 17h12. Le client appelle. "Mon site affiche 502 Bad Gateway, on perd des ventes." Vous ouvrez le site dans votre navigateur, effectivement, ecran blanc, message brutal :
502 Bad Gateway
nginx/1.24.0
C'est probablement l'erreur que j'ai debuggee le plus souvent dans ma carriere d'agent support. Une centaine de cas faciles, une vingtaine de cas tordus, deux ou trois qui m'ont fait perdre une journee entiere. A force, j'ai developpe une methode systematique. Cet article c'est exactement la procedure que je suis quand un ticket 502 arrive, dans l'ordre, sans sauter d'etape.
La 502 Bad Gateway veut dire une chose precise : Nginx, en tant que reverse proxy ou frontend, n'a pas reussi a obtenir une reponse valide du service backend (PHP-FPM, Node.js, Python uWSGI, Gunicorn, peu importe). C'est presque toujours un probleme cote backend, rarement cote Nginx lui-meme. Mais comme c'est Nginx qui affiche le message, on a tendance a accuser Nginx en premier. Erreur classique.
Etape 1 : Toujours les logs, toujours en premier
Avant de toucher quoi que ce soit, on lit les logs. C'est la premiere chose, la deuxieme, et la troisieme. 90% des 502 sont diagnostiquees en lisant les logs. Les 10% restants demandent du flair, mais on commence quand meme par les logs.
# Logs d'erreur Nginx (toujours en premier)
sudo tail -100 /var/log/nginx/error.log
# Logs specifiques au site si vous en avez configure
sudo tail -100 /var/log/nginx/monsite.error.log
# Logs PHP-FPM (si l'upstream est PHP)
sudo tail -100 /var/log/php8.2-fpm.log
# Logs systeme avec timestamp recent
sudo journalctl -u nginx --since "15 minutes ago" --no-pager
sudo journalctl -u php8.2-fpm --since "15 minutes ago" --no-pager
# Voir les logs en temps reel pendant que vous reproduisez l'erreur
sudo tail -f /var/log/nginx/error.log /var/log/php8.2-fpm.log
Les messages d'erreur Nginx les plus frequents pour une 502 :
# PHP-FPM mort ou inaccessible
connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory)
connect() to unix:/run/php/php8.2-fpm.sock failed (111: Connection refused)
# Timeout backend
upstream timed out (110: Connection timed out)
# Buffer trop petit
upstream sent too big header while reading response header from upstream
# Backend a ferme la connexion
upstream prematurely closed connection while reading response header
# Resolution DNS qui foire (sur les proxy_pass externes)
recv() failed (104: Connection reset by peer)
Copiez le message exact, c'est votre point de depart. Le reste de cet article passe en revue chaque cause par ordre de frequence.
Cause 1 : PHP-FPM est mort
La cause numero un, de loin. Sur les serveurs avec peu de RAM ou apres un deploiement qui a mal tourne, PHP-FPM crashe et Nginx n'a plus personne a qui parler.
sudo systemctl status php8.2-fpm
Si vous voyez Active: failed (Result: signal) ou Active: inactive (dead), c'est confirme. Verifiez aussi que le socket unix existe :
ls -la /run/php/php8.2-fpm.sock
# srw-rw---- 1 www-data www-data 0 May 7 14:32 /run/php/php8.2-fpm.sock
Pas de fichier = PHP-FPM ne tourne pas. Pour comprendre pourquoi il a crashe, allez dans /var/log/php8.2-fpm.log et cherchez le dernier WARNING ou ALERT :
sudo grep -E "WARNING|ALERT|ERROR" /var/log/php8.2-fpm.log | tail -20
Les causes typiques :
WARNING: [pool www] child 12345 exited on signal 9 (SIGKILL) after ...: OOM killer (manque de RAM)WARNING: [pool www] server reached pm.max_children setting (5): tous les workers sont occupesALERT: [pool www] pid 12345, please increase 'memory_limit' in php.ini: memoire PHP epuisee sur une requete
Redemarrer PHP-FPM en urgence :
sudo systemctl restart php8.2-fpm
sudo systemctl status php8.2-fpm
Si le service redemarre puis recrashe en quelques secondes, c'est un probleme de config. Testez la config avant de redemarrer :
sudo php-fpm8.2 -t
# [07-May-2026 14:35:21] NOTICE: configuration file /etc/php/8.2/fpm/php-fpm.conf test is successful
Cause 2 : mauvaise config upstream entre Nginx et PHP-FPM
Nginx envoie a un endroit, PHP-FPM ecoute ailleurs. Tres frequent quand on copie une config d'un serveur a un autre sans verifier.
Dans Nginx, votre bloc location ~ \.php$ doit ressembler a :
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
Ou si vous utilisez TCP au lieu d'un socket Unix :
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
Verifiez que PHP-FPM ecoute sur le meme endpoint :
sudo grep -E "^listen" /etc/php/8.2/fpm/pool.d/www.conf
# listen = /run/php/php8.2-fpm.sock
Si Nginx pointe sur le socket et PHP-FPM ecoute sur le port 9000, ca ne peut pas marcher. Alignez les deux. Sur Debian 12 et Ubuntu 24.04 par defaut, c'est le socket Unix /run/php/php8.2-fpm.sock. Sur certaines distributions ou docker images, c'est 127.0.0.1:9000. Adaptez Nginx en consequence.
Apres modification, testez et rechargez Nginx :
sudo nginx -t
sudo systemctl reload nginx
Permissions du socket
Meme si le chemin est bon, les permissions peuvent bloquer. Verifiez que Nginx (user www-data) peut lire/ecrire sur le socket :
ls -la /run/php/php8.2-fpm.sock
# srw-rw---- 1 www-data www-data 0 May 7 14:32 /run/php/php8.2-fpm.sock
Dans /etc/php/8.2/fpm/pool.d/www.conf :
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
Cause 3 : timeout backend
L'application met trop de temps a repondre, Nginx coupe et renvoie 502 (parfois 504, mais 502 aussi quand FastCGI termine prematurement). Typique sur les imports massifs, les exports CSV de 100 000 lignes, les rapports comptables annuels.
Dans le bloc Nginx :
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_read_timeout 300;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 300;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
300 secondes pour fastcgi_read_timeout c'est un bon compromis. Si vous avez vraiment des operations longues, montez a 600. Au-dela, c'est qu'il faut sortir le traitement en background (cron, queue, worker).
Pour un proxy Node.js :
location / {
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 300;
proxy_connect_timeout 60;
proxy_send_timeout 300;
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;
}
N'oubliez pas de monter aussi max_execution_time cote PHP dans /etc/php/8.2/fpm/pool.d/www.conf :
php_admin_value[max_execution_time] = 300
Si PHP coupe avant Nginx, vous aurez quand meme des 502.
Cause 4 : la RAM est epuisee, OOM killer en action
C'est le scenario typique du VPS a 2 Go de RAM avec WordPress + WooCommerce + 30 plugins + MariaDB + Redis. Tout fonctionne bien pendant 3 jours, puis sous un pic de trafic, l'OOM killer flingue PHP-FPM (ou MariaDB, ou les deux).
# Verifier l'OOM killer recemment
sudo dmesg -T | grep -i "out of memory"
sudo dmesg -T | grep -i "killed process"
# Logs du noyau
sudo journalctl -k --since "1 hour ago" | grep -i oom
# Memoire disponible actuelle
free -h
# total used free shared buff/cache available
# Mem: 1.9Gi 1.7Gi 102Mi 12Mi 155Mi 90Mi
# Swap: 2.0Gi 1.2Gi 820Mi
Si available est en dessous de 100 Mo et que le swap est utilise massivement, vous etes au bord de la noyade.
Remediations dans /etc/php/8.2/fpm/pool.d/www.conf :
pm = ondemand
pm.max_children = 8
pm.process_idle_timeout = 10s
pm.max_requests = 500
La formule pour calculer pm.max_children : (RAM disponible apres tout le reste) / memoire moyenne par process PHP. En general 30 a 60 Mo par process PHP-FPM en charge sur WordPress moderne. Avec 1 Go libre pour PHP, on est bien autour de 16-20 workers max. Sur un VPS de 2 Go, plutot 6-10.
pm.max_requests = 500 recycle le worker apres 500 requetes, ce qui evite les fuites memoire qui s'accumulent (notamment avec certains plugins).
Ajouter du swap si vous n'en avez pas :
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Le swap ne remplace pas la RAM mais evite les crashs durs pendant les pics.
Cause 5 : buffers trop petits
Message typique dans error.log :
upstream sent too big header while reading response header from upstream
L'application backend renvoie une reponse avec un gros header (cookies de session enormes, CSRF tokens, debug bar) et le buffer Nginx ne suit pas.
# Pour FastCGI (PHP-FPM)
fastcgi_buffer_size 32k;
fastcgi_buffers 8 16k;
fastcgi_busy_buffers_size 32k;
# Pour proxy_pass (Node.js, Python)
proxy_buffer_size 32k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
Mettez ca dans le bloc http {} de /etc/nginx/nginx.conf pour que ca s'applique partout, ou directement dans le bloc location concerne. nginx -t puis systemctl reload nginx.
Cause 6 : application Node.js ou Python qui plante
Si vous proxifiez une appli Node.js ou Python, elle peut crasher pour des raisons applicatives sans toucher a Nginx ni PHP.
# Service systemd dedie
sudo systemctl status monapp
sudo journalctl -u monapp --since "15 minutes ago" --no-pager
# Avec PM2
pm2 list
pm2 logs monapp --lines 100
pm2 restart monapp
# Avec Docker
docker ps
docker logs --tail 100 mon_container
docker restart mon_container
Les causes typiques cote Node.js : out of memory (la heap V8 par defaut est limitee), exception non catchee qui kill le process, dependance qui crash au demarrage apres une mise a jour.
Protegez votre service avec un restart automatique :
# /etc/systemd/system/monapp.service
[Service]
Restart=always
RestartSec=5
Ou avec PM2 :
pm2 start app.js --name monapp --max-memory-restart 500M
pm2 save
pm2 startup
La checklist diagnostic 502 en 60 secondes
Quand le ticket urgent arrive, voici ma sequence en moins d'une minute :
# 1. Services up ?
sudo systemctl status nginx php8.2-fpm
# 2. Config Nginx valide ?
sudo nginx -t
# 3. Logs recents ?
sudo tail -30 /var/log/nginx/error.log
# 4. RAM ?
free -h
# 5. Disque plein ?
df -h
# 6. OOM killer recent ?
sudo dmesg -T | tail -20 | grep -i oom
# 7. Processus en cours ?
ps aux | grep -E "php-fpm|nginx" | head -20
# 8. Restart en derniere extremite
sudo systemctl restart php8.2-fpm nginx
Si ca repart apres restart mais retombe sous charge, c'est un probleme de capacite (max_children, RAM, max_connections MariaDB). Dimensionnez en consequence.
Erreurs courantes et leur fix
502 intermittentes (pas permanentes). C'est presque toujours pm.max_children trop bas. PHP-FPM n'a plus de worker libre, refuse les nouvelles connexions, Nginx renvoie 502. Augmentez pm.max_children selon la formule plus haut.
502 uniquement sur certaines pages (admin WP, exports). Timeout. Augmentez fastcgi_read_timeout et max_execution_time. Si c'est juste sur un export massif, mieux vaut le sortir du flux web et le faire en cron ou queue.
502 apres une mise a jour de PHP. Le service php8.2-fpm n'existe plus parce que vous etes passe en PHP 8.3. Updatez votre conf Nginx vers php8.3-fpm.sock et restartez.
502 random apres un certain temps d'uptime. Fuite memoire dans une appli Node.js ou un plugin WP. Trouvez le coupable avec htop, ou imposez un recyclage automatique avec pm.max_requests (PHP) ou --max-memory-restart (PM2).
502 avec Cloudflare devant. Le cloud Cloudflare renvoie sa propre 502 si votre serveur ne repond pas. Verifiez les logs cote serveur d'abord, le probleme est presque toujours la, pas chez Cloudflare. Le cf-ray dans les headers de la 502 confirme que ca vient de chez eux mais que le backend (votre serveur) est en cause.
Pour aller plus loin
- Diagnostiquer les erreurs 500 sur mutualise
- Reverse proxy Nginx avec SSL
- Resoudre les boucles de redirection SSL Cloudflare
- Logs Linux, ou chercher et comment lire
- Optimiser les performances de WordPress
Conclusion : la 502 ne ment jamais, vos logs non plus
Une 502 c'est toujours une histoire de communication ratee entre Nginx et le service derriere. Quand on suit la procedure dans l'ordre (logs, services, config, RAM, timeouts), on trouve la cause en moins de 10 minutes dans 95% des cas. Les 5% restants c'est en general des trucs exotiques (DNS resolver Nginx qui meurt, conntrack table pleine, mauvaise reglage SELinux), mais ca arrive moins d'une fois par an.
La morale, c'est que pour debugger sereinement il faut deux choses : des logs actives partout (Nginx, PHP-FPM, application), et un monitoring qui vous previent AVANT que le client appelle. Netdata ou Grafana suffisent pour avoir une alerte sur la RAM disponible ou le nombre de workers PHP-FPM occupes. Mieux vaut etre alerte avant la 502 que de la voir en production.