nginx en 502 Bad Gateway avec php-fpm : socket absent, droits refusés ou port 9000 fermé

Un 502 vient de nginx, pas de PHP : c’est la connexion à php-fpm qui a échoué
nginx renvoie 502 Bad Gateway quand la connexion vers php-fpm n’aboutit pas, et jamais parce qu’un script PHP a produit une erreur. La documentation nginx rattache ce code au module FastCGI, qui le renvoie lorsque le serveur amont n’est pas joignable ou ferme la connexion avant d’avoir écrit ses en-têtes. Sous Debian 12, le paquet php8.2-fpm crée le socket /run/php/php8.2-fpm.sock, et l’adresse 127.0.0.1:9000 prend le relais quand le pool est configuré en TCP. Deux causes expliquent la majorité des cas, un fastcgi_pass qui ne vise plus le socket réellement présent, et des droits insuffisants sur ce fichier. Le journal /var/log/nginx/error.log écrit la cause en clair, avec l’erreur système entre parenthèses, comme (2: No such file or directory) ou (13: Permission denied). Tout le diagnostic se fait donc sur le serveur, sans ouvrir une ligne du code du site.
Sommaire
- Un 502 vient de nginx, pas de PHP : c’est la connexion à php-fpm qui a échoué
- Six vérifications, dans cet ordre, avec ce que l’écran doit afficher
- Le socket change de nom à chaque montée de version de PHP, fastcgi_pass non
- (13: Permission denied) arrive quand le socket existe mais reste fermé au compte nginx
- Un pool sur 127.0.0.1:9000 échoue autrement, et cinq critères séparent les deux méthodes
- 502, 504 et 403 ne désignent pas la même panne, et trois cas sortent du tableau
- Socket, port, redémarrage : six décisions après le diagnostic
- Sources
sudo tail -n 30 /var/log/nginx/error.log
La ligne qui décide de la suite commence par connect() to et se termine par while connecting to upstream. Deux formes reviennent sur une Debian 12.
connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory) while connecting to upstream
connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied) while connecting to upstream
nginx assemble ce message avec le numéro et le texte d’erreur de l’appel système, un format que la documentation nginx illustre dans la directive error_log avec errno 111 et errtext Connection refused. Ce numéro tranche entre fichier absent et accès refusé.
Six vérifications, dans cet ordre, avec ce que l’écran doit afficher
Six commandes séparent un 502 d’une correction, et leur ordre compte : la première oriente vers un chemin de socket ou vers un problème de droits, les suivantes confirment. Le serveur de référence est une Debian 12 avec nginx et le paquet php8.2-fpm des dépôts, et selon les notes de version Ubuntu 24.04, le paquet PHP y est en 8.3.6 avec nginx 1.24, seuls les numéros changent. Aucune commande ne modifie la configuration, elles s’exécutent sur un serveur en production. Le service arrêté après un redémarrage de machine tombe à la deuxième étape. La troisième compare le chemin écrit dans nginx au fichier présent sur le disque. Vient ensuite la lecture des droits du socket, puis celle du démarrage de php-fpm. La dernière ne concerne que les serveurs dont le pool écoute en réseau.
- Lire l’erreur exacte.
sudo tail -n 30 /var/log/nginx/error.logAttendu : une ligne connect() to avec le chemin ou l’adresse testés et un numéro d’erreur entre parenthèses.
- Vérifier les deux services.
systemctl is-active nginx systemctl is-active php8.2-fpmAttendu : active sur les deux lignes. Toute autre valeur arrête le diagnostic ici.
- Comparer le chemin de fastcgi_pass au socket présent.
sudo nginx -T | grep -n fastcgi_pass ls -l /run/php/Attendu : le chemin affiché par nginx figure tel quel dans la liste de /run/php/.
- Lire les droits du socket.
stat -c "%A %U %G" /run/php/php8.2-fpm.sockAttendu : un mode srw-rw—- et un groupe auquel www-data appartient.
- Contrôler le démarrage de php-fpm.
journalctl -u php8.2-fpm -n 20 --no-pagerAttendu : des lignes NOTICE avec fpm is running puis ready to handle connections.
- Si le pool écoute en réseau, vérifier le port.
sudo ss -ltnp | grep 9000Attendu : une ligne LISTEN sur 127.0.0.1:9000, dont la dernière colonne nomme php-fpm8.2.
| Message littéral dans error.log | Numéro d’erreur | Cause constatée | Correction |
|---|---|---|---|
| connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory) | 2 | le chemin écrit dans fastcgi_pass ne correspond à aucun socket | aligner fastcgi_pass sur le nom affiché par ls /run/php/, puis nginx -t et reload |
| connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied) | 13 | socket présent, droits ou groupe insuffisants pour le compte nginx | corriger listen.owner, listen.group et listen.mode dans pool.d/www.conf, puis redémarrer php8.2-fpm |
| connect() to 127.0.0.1:9000 failed (111: Connection refused) | 111 | aucun processus n’écoute sur ce port | démarrer php8.2-fpm ou corriger la directive listen du pool |
| FastCGI sent in stderr: “Primary script unknown” | aucun | SCRIPT_FILENAME désigne un fichier qui n’existe pas | vérifier root et la ligne fastcgi_param SCRIPT_FILENAME |
La cinquième étape s’appuie sur journalctl, décrit avec les journaux d’un service systemd.
Le socket change de nom à chaque montée de version de PHP, fastcgi_pass non
Debian et Ubuntu inscrivent le numéro de branche dans le nom du socket, ce qui casse le lien entre nginx et php-fpm à chaque montée de version. Le paquet php8.2-fpm crée /run/php/php8.2-fpm.sock, son remplaçant php8.4-fpm crée /run/php/php8.4-fpm.sock, et la ligne fastcgi_pass du fichier de site continue de viser l’ancien chemin, désormais vide. nginx répond alors 502 sur toutes les pages PHP du site, pas sur une seule. Cette mécanique suit le calendrier des branches, et le manuel PHP date la branche 8.2 du 8 décembre 2022, avec un support de sécurité jusqu’au 31 décembre 2026. Le wiki Arch Linux retient un choix opposé, un socket unique /run/php-fpm/php-fpm.sock sans numéro de version, qui survit aux mises à jour. Cette divergence entre distributions explique pourquoi la panne revient au rythme des changements de branche.
ls -l /run/php/
sudo nginx -T | grep -n fastcgi_pass
L’écart entre les deux sorties donne la correction. Un fichier php8.4-fpm.sock dans /run/php/ face à un fastcgi_pass sur php8.2-fpm.sock signifie une ligne à réécrire dans /etc/nginx/sites-available/.
sudo nginx -t
sudo systemctl reload nginx
Le test de syntaxe doit afficher test is successful avant tout rechargement. Un nginx -t en échec laisse la configuration précédente active.
(13: Permission denied) arrive quand le socket existe mais reste fermé au compte nginx
Le refus de permission sur le socket suit un changement de compte plutôt qu’une mise à jour. D’après le manuel PHP, les trois directives décisives vivent dans le pool, listen.owner, listen.group et listen.mode, et ce dernier vaut 0660 par défaut, ce qui ouvre le fichier au propriétaire et à son groupe, personne d’autre. Sur une Debian 12, le pool www de /etc/php/8.2/fpm/pool.d/www.conf porte user = www-data et group = www-data, et nginx doit tourner sous un compte membre du même groupe. Un nginx compilé à la main, lancé en nobody, ou un pool modifié sans redémarrage, produit un 502 immédiat sur chaque page PHP. Le message ne nomme jamais le compte refusé.
ps -o user= -C nginx | sort -u
stat -c "%A %U %G" /run/php/php8.2-fpm.sock
Deux comptes ressortent de la première commande sur une installation classique, root pour le processus maître et www-data pour les workers. La seconde affiche un mode srw-rw—- et un couple propriétaire/groupe qui doit contenir www-data. Un groupe différent, php-fpm par exemple, laisse nginx dehors malgré des droits corrects sur le papier.
Corriger listen.mode ou listen.group dans le pool puis redémarrer php8.2-fpm recrée le socket avec les nouveaux droits. Un rechargement de nginx seul n’a aucun effet, le fichier appartient à php-fpm. Le détail des groupes se lit dans les comptes et permissions sous Linux.
Un pool sur 127.0.0.1:9000 échoue autrement, et cinq critères séparent les deux méthodes
Un pool posé sur 127.0.0.1:9000 tombe en panne pour d’autres raisons qu’un pool sur socket, et Connection refused en est la signature. Le manuel PHP recommande le socket Unix quand le serveur web et php-fpm partagent la machine, l’accès étant alors réglé par listen.owner, listen.group et listen.mode. En TCP, la directive listen.allowed_clients n’a pas de valeur par défaut et accepte donc n’importe quelle adresse source, un point que le même manuel signale. Deux situations justifient le port 9000, un php-fpm installé sur une autre machine et un pool exécuté dans un conteneur. Un port déjà occupé par un service tiers produit le même 502 sans que php-fpm soit en cause. La commande ss désigne en une seconde le processus qui détient le port, et une simple lecture du pool dit lequel des deux modes est actif.
grep -n "listen" /etc/php/8.2/fpm/pool.d/www.conf
| Méthode d’écoute | Chemin ou adresse sous Debian 12 | Droits appliqués | Nom après une mise à jour de PHP | Accès depuis une autre machine |
|---|---|---|---|---|
| socket Unix | /run/php/php8.2-fpm.sock | listen.owner, listen.group, listen.mode, 0660 par défaut | change avec la branche, php8.2 puis php8.4 | non |
| port TCP | 127.0.0.1:9000 | listen.allowed_clients, sans valeur par défaut | inchangé | oui, avec allowed_clients restreint |
Deux colonnes décident du choix. Le nom du socket bouge à chaque branche PHP, l’adresse du port reste fixe, et l’accès depuis une autre machine n’existe qu’en TCP.
502, 504 et 403 ne désignent pas la même panne, et trois cas sortent du tableau
Le code 502 ne couvre qu’une partie des échecs entre nginx et php-fpm, et confondre les codes fait perdre du temps sur la mauvaise piste. La documentation nginx fixe fastcgi_connect_timeout, fastcgi_send_timeout et fastcgi_read_timeout à 60 secondes chacun, et leur dépassement produit un 504 Gateway Time-out, pas un 502. Un 403 Forbidden signale l’inverse, une réponse reçue puis refusée, souvent par la directive security.limit_extensions du pool, que le manuel PHP laisse à .php .phar. Trois cas échappent à cette lecture, un pare-feu local qui bloque le port 9000, un profil AppArmor qui refuse l’accès au socket, et un pool saturé qui n’accepte plus de connexion. Le wiki Arch Linux décrit un profil AppArmor php-fpm par défaut qui interdit l’accès aux fichiers hors des chemins système.
Une donnée reste hors de portée des sources publiques. Le contenu de l’unité systemd php8.2-fpm.service n’est pas publié dans les pages consultées ici ; systemctl cat php8.2-fpm l’affiche sur la machine concernée.
La saturation du pool produit un 502 sans erreur système. Le manuel PHP rend pm.max_children obligatoire et ne lui donne aucune valeur par défaut, alors que pm.start_servers se calcule comme la moyenne de pm.min_spare_servers et pm.max_spare_servers. Un refus AppArmor se lit hors de nginx, une ligne DENIED dans dmesg nommant le profil et le chemin refusé. Les autres pannes de service sous Debian et Ubuntu se rejoignent dans la rubrique Linux.
Socket, port, redémarrage : six décisions après le diagnostic
Quelle ligne changer pour basculer du socket vers 127.0.0.1:9000 ?
Deux fichiers vont ensemble. Remplacez dans le pool listen = /run/php/php8.2-fpm.sock par listen = 127.0.0.1:9000, redémarrez php8.2-fpm, puis passez fastcgi_pass sur 127.0.0.1:9000 dans le fichier de site. Modifier un seul des deux côtés prolonge la panne.
Socket Unix ou port 9000 : lequel choisir quand les deux fonctionnent ?
Le manuel PHP recommande le socket Unix lorsque nginx et php-fpm partagent la machine, l’accès étant réglé par listen.owner, listen.group et listen.mode. Le wiki Arch Linux ajoute que les sockets de domaine Unix sont plus rapides. Le port 9000 ne se justifie que pour un php-fpm distant ou conteneurisé, avec listen.allowed_clients restreint.
Combien de requêtes sont perdues pendant la correction ?
Un sudo systemctl reload nginx applique la configuration sans arrêter le service et laisse finir les requêtes en cours. Un systemctl restart php8.2-fpm coupe celles qui sont en traitement, parce que le socket est recréé et les workers remplacés. Le changement de listen impose ce redémarrage, à lancer sur un créneau creux.
Pourquoi le 502 revient-il à chaque mise à jour de PHP ?
Parce que Debian et Ubuntu inscrivent la branche dans le nom du socket, php8.2-fpm puis php8.4-fpm, alors que fastcgi_pass garde l’ancien chemin en dur. Après chaque montée de version, comparez ls -l /run/php/ et sudo nginx -T | grep fastcgi_pass. Le manuel PHP documente aussi listen = /var/run/php-fpm-${POOL_NAME}.sock, qui évite un chemin figé.
nginx n’écrit rien de plus : où lire ce que php-fpm a vu ?
Le pool et le fichier principal portent chacun une directive error_log. Relevez le chemin avec grep -n “error_log” /etc/php/8.2/fpm/php-fpm.conf /etc/php/8.2/fpm/pool.d/www.conf, puis lisez les dernières lignes du fichier obtenu. Le manuel PHP fixe log_level à notice et request_slowlog_timeout à 0 par défaut.
Un certificat TLS peut-il provoquer ce 502 ?
Non, une erreur de certificat bloque la poignée de main ou déclenche un avertissement du navigateur, avant toute requête HTTP. Le 502 naît de la couche FastCGI, en aval du déchiffrement. Si curl -H “Host: exemple.org” http://127.0.0.1/ renvoie 502 et que error.log affiche connect() to, cherchez le socket ou le port.