Quand WordPress renvoie un 502 ou 504, l’entreprise ne voit pas « un problème de connexion à l’upstream ». Elle voit « paiement cassé », « publicités hors ligne », ou « le billet du PDG ne se publie pas ». Vous voyez l’autre face : Nginx entre Internet et PHP‑FPM comme un videur qui oublie parfois la liste des invités.
Ce guide de terrain couvre les erreurs de configuration Nginx qui provoquent systématiquement des 5xx sur WordPress, comment les confirmer avec des commandes, et quoi changer sans transformer un incident transitoire en carrière à plein temps.
Faits intéressants et contexte (pourquoi ça revient)
- Nginx a été créé pour résoudre le problème C10k (traiter 10 000 connexions concurrentes) et repose sur une I/O pilotée par événements. WordPress, de son côté, est une application PHP avec base de données qui aime le travail synchrone. Ensemble, vous obtenez un serveur web très efficace exposant un chemin de requête peu efficace.
- « Bad Gateway » n’est pas une erreur morale de Nginx. C’est une affirmation : Nginx a demandé une réponse à un upstream (PHP‑FPM) et n’a pas obtenu quelque chose d’utilisable.
- La plupart des incidents 5xx WordPress sont auto‑infligés : timeouts, valeurs par défaut de buffers, incompatibilités de permissions et mauvaises hypothèses sur la montée en charge de PHP‑FPM. Il existe des bugs logiciels, mais les erreurs de config reviennent le plus souvent.
- PHP‑FPM est un gestionnaire de processus, pas de la magie. Si vous lui donnez trop peu de workers, il met en file d’attente. Si vous en donnez trop, il épuise la mémoire et meurt. Les deux peuvent ressembler à « Nginx est cassé ».
- HTTP/2 n’a pas réduit le travail serveur pour les pages dynamiques ; il a réduit la surcharge de connexion et amélioré le multiplexage. Un client peut maintenant lancer plusieurs requêtes concurrentes sur une même connexion, ce qui change la forme du trafic et peut exposer plus vite la saturation de FPM.
- admin-ajax.php de WordPress est une petite URL au grand rayon d’impact. Les plugins l’utilisent pour tout, y compris des tâches longues. Si vous le traitez comme une page normale, vous découvrirez des 504 sur des actions admin « aléatoires ».
- Les en‑têtes ont grossi au fil des années. L’encombrement des cookies dû aux plugins, A/B testing et analytics peut déclencher « upstream sent too big header » et apparaître comme 502/500 selon le chemin d’échec exact.
- Les timeouts par défaut sont rarement alignés. Nginx a ses timeouts, PHP‑FPM a ses timeouts de requête, et votre base de données a des attentes de verrou. Le désalignement crée les mystères classiques « ça meurt exactement à 60 secondes ».
Une idée paraphrasée souvent attribuée à Werner Vogels (opérations et fiabilité) : Tout échoue ; le travail consiste à concevoir pour l’échec et à récupérer rapidement.
Blague #1 : La seule chose plus fiable qu’un upstream Nginx mal configuré est un canal Slack qui se remplit de « quelqu’un d’autre voit des 502 ? » en moins de 30 secondes.
Playbook de diagnostic rapide (premiers/seconds/troisièmes contrôles)
Quand un site WordPress commence à renvoyer des 5xx, vous ne commencez pas par réécrire tout le bloc serveur. Vous faites un triage. Vous identifiez si la panne est de routage, de santé de l’upstream, de capacité, ou de politique (permissions/limites). Ce playbook est le chemin le plus court que je connaisse pour « corriger ou isoler ».
Premier : identifier quel 5xx et où il est généré
- 502/504 indique généralement un problème d’upstream (PHP‑FPM, réseau, socket, timeouts).
- 500 peut être un crash upstream, une erreur fatale du script, une boucle de réécriture, ou une erreur interne Nginx.
- 503 est souvent un throttling, un mode maintenance, un upstream marqué comme indisponible, ou une exhaustion de capacité.
Deuxième : consulter les logs d’erreur qui disent la vérité
- Log d’erreurs Nginx pour erreurs d’upstream, problèmes de buffers, boucles de réécriture, problèmes de permissions.
- Log PHP‑FPM pour « server reached pm.max_children », requêtes lentes, segfaults et workers tués.
- Journal du noyau / systemd pour OOM kills, redémarrages, exhaustion de descripteurs de fichiers.
Troisième : décider si c’est un bogue sur une seule requête ou un événement de capacité/file d’attente
- Bogue sur une seule requête : une seule URL échoue ; les autres pages fonctionnent ; l’erreur se reproduit instantanément. Pensez réécriture, permissions, fatal script, protection path traversal, comportement d’un plugin spécifique.
- Capacité/file d’attente : 504 généralisés, réponses lentes, temps de connexion upstream en hausse, FPM saturé, base lente. Pensez tuning pm, timeouts, verrous DB, ou pic de trafic.
Quatrième : choisir une atténuation sûre
- Augmenter provisoirement le niveau de logs (pas en permanence).
- Augmenter les timeouts spécifiques avec prudence seulement quand ils correspondent à la réalité.
- Monter/augmenter la capacité FPM si la RAM le permet.
- Désactiver la route de plugin qui exécute 2 minutes de travail via HTTP.
Tâches pratiques : commandes, sortie attendue et décisions (12+)
Ce sont les commandes que j’exécute quand le pager sonne. Chacune inclut ce que la sortie signifie et la décision à en tirer. Exécutez‑les sur l’hôte Nginx sauf indication contraire.
Task 1: Confirm Nginx config parses cleanly
cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Ce que ça signifie : La syntaxe est valide. Cela ne garantit pas que la config est correcte, seulement qu’elle est analysable.
Décision : Si cela échoue, arrêtez. Corrigez la syntaxe avant de poursuivre le débogage d’évents 5xx qui ne sont que le résultat d’un rechargement raté.
Task 2: Check whether Nginx actually loaded your latest config
cr0x@server:~$ sudo systemctl status nginx --no-pager
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled)
Active: active (running) since Fri 2025-12-27 09:12:41 UTC; 3h 10min ago
Docs: man:nginx(8)
Main PID: 1327 (nginx)
Tasks: 2 (limit: 18962)
Memory: 8.9M
CGroup: /system.slice/nginx.service
├─1327 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
└─1330 nginx: worker process
Ce que ça signifie : Nginx est en cours d’exécution. La ligne de commande du processus master peut révéler s’il utilise un chemin de config alternatif.
Décision : S’il n’est pas actif ou est en crash-loop, corrigez cela d’abord. Si vous avez modifié des fichiers mais n’avez pas rechargé, vous déboguez la config d’hier.
Task 3: Tail Nginx error log and reproduce a failing request
cr0x@server:~$ sudo tail -n 50 /var/log/nginx/error.log
2025/12/27 12:18:07 [error] 1330#1330: *481 upstream prematurely closed connection while reading response header from upstream, client: 203.0.113.19, server: example.com, request: "GET /wp-admin/ HTTP/2.0", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "example.com"
2025/12/27 12:18:08 [error] 1330#1330: *482 connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 203.0.113.19, server: example.com, request: "GET /wp-login.php HTTP/2.0", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "example.com"
Ce que ça signifie : Deux causes racines différentes apparaissent déjà : une fermeture prématurée upstream (worker FPM mort/timeout) et un problème de permission du socket.
Décision : Ne devinez pas. Prenez la première ligne d’erreur répétée et corrigez ce mode d’échec spécifique en priorité.
Task 4: Confirm PHP-FPM is up and listening where Nginx thinks it is
cr0x@server:~$ sudo systemctl status php8.2-fpm --no-pager
● php8.2-fpm.service - The PHP 8.2 FastCGI Process Manager
Loaded: loaded (/lib/systemd/system/php8.2-fpm.service; enabled)
Active: active (running) since Fri 2025-12-27 09:11:02 UTC; 3h 12min ago
Docs: man:php-fpm8.2(8)
Process: 1051 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php8.2-fpm.sock /etc/php/8.2/fpm/pool.d/www.conf 82 (code=exited, status=0/SUCCESS)
Main PID: 1018 (php-fpm8.2)
Tasks: 18 (limit: 18962)
Memory: 312.4M
CGroup: /system.slice/php8.2-fpm.service
├─1018 php-fpm: master process (/etc/php/8.2/fpm/php-fpm.conf)
├─1032 php-fpm: pool www
└─1033 php-fpm: pool www
Ce que ça signifie : Le service est vivant. Cela ne confirme pas encore que Nginx peut atteindre le socket, seulement que FPM n’a pas disparu.
Décision : S’il est inactif ou redémarre, investiguez la cause (OOM, erreur de config). S’il est actif, vérifiez le chemin du socket et les permissions ensuite.
Task 5: Inspect the FastCGI socket permissions
cr0x@server:~$ ls -l /run/php/php8.2-fpm.sock
srw-rw---- 1 www-data www-data 0 Dec 27 09:11 /run/php/php8.2-fpm.sock
Ce que ça signifie : Seuls root et les membres du groupe www-data peuvent se connecter. Si les workers Nginx tournent en tant que nginx, ils recevront un permission denied.
Décision : Faites tourner Nginx en tant que www-data (commun sur Debian/Ubuntu) ou changez le propriétaire/groupe du pool FPM pour correspondre à l’utilisateur Nginx. Ne réglez pas ça en faisant « chmod 777 » : ce serait un incident de sécurité.
Task 6: Verify Nginx worker user and match it to the socket
cr0x@server:~$ grep -R "^\s*user\s" /etc/nginx/nginx.conf
user www-data;
Ce que ça signifie : Les workers Nginx tournent en www-data, donc le socket ci‑dessus doit être accessible.
Décision : Si l’utilisateur Nginx diffère (par ex. nginx), changez l’utilisateur Nginx ou la propriété du socket FPM ; gardez la cohérence entre hôtes.
Task 7: See whether PHP-FPM is saturated (pm.max_children reached)
cr0x@server:~$ sudo grep -R "pm.max_children" /etc/php/8.2/fpm/pool.d/www.conf
pm.max_children = 20
cr0x@server:~$ sudo tail -n 30 /var/log/php8.2-fpm.log
[27-Dec-2025 12:21:44] WARNING: [pool www] server reached pm.max_children setting (20), consider raising it
Ce que ça signifie : Les requêtes font file d’attente sur FPM. Nginx voit des upstream lents et commence à timeouter ou à échouer les connexions.
Décision : Soit augmentez pm.max_children (si vous avez de la RAM disponible), réduisez le coût par requête (cache, DB), ou scalez horizontalement. L’augmenter sans analyse vous fera échanger des 504 contre des OOM kills.
Task 8: Confirm the exact HTTP status and timing from the edge
cr0x@server:~$ curl -sS -o /dev/null -w "code=%{http_code} ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com/wp-admin/
code=504 ttfb=60.002 total=60.002
Ce que ça signifie : Un délai de 60 secondes suggère un timeout Nginx (souvent fastcgi_read_timeout selon certaines configs) ou un upstream qui se bloque systématiquement.
Décision : Si l’échec est un nombre rond net, cherchez les timeouts et la mise en file d’attente. S’il échoue instantanément, cherchez permissions, sockets manquants, ou fatals PHP immédiats.
Task 9: Validate your WordPress routing (try_files) isn’t wrong
cr0x@server:~$ sudo nginx -T 2>/dev/null | sed -n '/server_name example.com/,/}/p' | sed -n '1,140p'
server {
server_name example.com;
root /var/www/example.com/public;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}
Ce que ça signifie : Le modèle canonique est présent : servir le statique s’il existe, sinon router vers index.php avec les arguments de requête.
Décision : Si vous voyez try_files $uri /index.php; sans $args, attendez‑vous à des comportements aléatoires de l’application et des plugins. Si vous voyez une récursion (routage vers une URI qui retombe dans le même bloc location), attendez‑vous à des 500 dus à des boucles de redirection interne.
Task 10: Check for “upstream sent too big header” (cookie bloat)
cr0x@server:~$ sudo grep -R "too big header" -n /var/log/nginx/error.log | tail -n 5
/var/log/nginx/error.log:1928:2025/12/27 11:04:15 [error] 1330#1330: *211 upstream sent too big header while reading response header from upstream, client: 198.51.100.77, server: example.com, request: "GET /wp-admin/ HTTP/2.0", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "example.com"
Ce que ça signifie : Nginx n’a pas pu contenir les en‑têtes upstream dans les buffers configurés. Les pages d’admin WordPress et certains plugins sont souvent en cause à cause d’encombrement des cookies et de redirections.
Décision : Augmentez les buffers FastCGI dans le serveur/location spécifique ; réduisez aussi l’encombrement des cookies si possible. N’augmentez pas globalement sans raison ; vous gonfleriez la mémoire sous charge.
Task 11: Find if you’re dropping uploads (413) but users report it as “5xx”
cr0x@server:~$ sudo grep -R "client intended to send too large body" -n /var/log/nginx/error.log | tail -n 3
/var/log/nginx/error.log:2210:2025/12/27 10:31:19 [error] 1330#1330: *302 client intended to send too large body: 134217728 bytes, client: 203.0.113.58, server: example.com, request: "POST /wp-admin/async-upload.php HTTP/2.0", host: "example.com"
Ce que ça signifie : C’est un 413, pas un 5xx, mais en tickets ça devient « upload échoue, site cassé ».
Décision : Définissez client_max_body_size à une valeur sensée pour votre activité et alignez‑la avec les limites PHP (upload_max_filesize, post_max_size).
Task 12: Detect OOM kills that look like random 502s
cr0x@server:~$ sudo journalctl -k --since "2 hours ago" | grep -i -E "oom|killed process" | tail -n 10
Dec 27 11:58:22 server kernel: Out of memory: Killed process 1033 (php-fpm) total-vm:1324080kB, anon-rss:512000kB, file-rss:0kB, shmem-rss:0kB
Ce que ça signifie : Le noyau a tué un worker PHP‑FPM. Nginx rapporte une fermeture upstream prématurée ou un 502. C’est intermittent parce que ça dépend de la pression mémoire.
Décision : Arrêtez d’augmenter pm.max_children sans mesure. Réduisez le nombre de workers, corrigez des fuites mémoire (plugins), ajoutez de la RAM, ou isolez les charges. OOM est la façon peu diplomatique du serveur pour dire « non ».
Task 13: Confirm file descriptor limits (a quiet 5xx factory)
cr0x@server:~$ sudo cat /proc/$(pidof nginx | awk '{print $1}')/limits | grep "Max open files"
Max open files 1024 524288 files
Ce que ça signifie : La limite souple est à 1024. Sous des rafales de keepalive + HTTP/2 + sockets upstream, ce n’est pas généreux.
Décision : Augmentez la limite souple via des overrides systemd et worker_rlimit_nofile dans Nginx. Puis validez sous charge. Si vous ignorez ça, vous poursuivrez des échecs upstream « aléatoires » pendant des semaines.
Task 14: Check upstream connect failures (socket path wrong or not created)
cr0x@server:~$ sudo grep -R "fastcgi_pass" -n /etc/nginx/sites-enabled | head
/etc/nginx/sites-enabled/example.com.conf:42: fastcgi_pass unix:/run/php/php8.2-fpm.sock;
cr0x@server:~$ test -S /run/php/php8.2-fpm.sock; echo $?
0
Ce que ça signifie : Le code de sortie 0 signifie que le socket existe et est un fichier socket. S’il retourne 1, Nginx pointe vers un fantasme.
Décision : Corrigez le chemin du socket ou passez à TCP (127.0.0.1:9000) si vous avez besoin de connectivité cross‑container/namespace. Mais gardez‑le cohérent et documenté.
Task 15: Identify slow PHP requests and match them to Nginx timeouts
cr0x@server:~$ sudo grep -R "request_slowlog_timeout" /etc/php/8.2/fpm/pool.d/www.conf
request_slowlog_timeout = 5s
cr0x@server:~$ sudo tail -n 20 /var/log/php8.2-fpm/www-slow.log
[27-Dec-2025 12:19:11] [pool www] pid 1099
script_filename = /var/www/example.com/public/wp-admin/admin-ajax.php
[0x00007f2b9c8a2e10] mysqli_query() /var/www/example.com/public/wp-includes/wp-db.php:2056
Ce que ça signifie : PHP passe du temps dans des requêtes DB. Nginx attend. Si votre timeout Nginx est plus court que le runtime d’une requête lente, vous obtiendrez des 504.
Décision : Corrigez la lenteur (DB/index/plugin) avant d’augmenter les timeouts. Des timeouts plus grands sans capacité mènent à une panne en mode lenteur plutôt qu’un échec rapide.
Erreurs courantes : symptômes → cause racine → correctif
Ceci est la section « je saigne ; quelle artère est touchée ? ». Lisez le symptôme, validez avec les logs, appliquez le correctif ciblé.
1) 502 Bad Gateway immédiatement sur les pages PHP
Symptômes : Les assets statiques se chargent. Toute route .php échoue instantanément. Le log d’erreurs Nginx montre connect() ... failed (2: No such file or directory) ou (13: Permission denied).
Cause racine : Mauvais chemin fastcgi_pass, socket manquant (FPM non démarré), ou incompatibilité des permissions du socket entre l’utilisateur Nginx et le pool FPM.
Correctif : Alignez fastcgi_pass sur le socket réel, assurez‑vous que FPM est actif, et configurez le pool FPM avec des directives comme :
listen = /run/php/php8.2-fpm.sock,
listen.owner = www-data,
listen.group = www-data,
listen.mode = 0660.
Rechargez FPM et Nginx.
2) 504 Gateway Timeout à des bornes temporelles constantes (30s/60s/120s)
Symptômes : Le site « fonctionne mais se met parfois à timeouter », souvent à une valeur ronde. Le log Nginx montre upstream timed out.
Cause racine : Timeouts désalignés : fastcgi_read_timeout Nginx trop bas pour le temps réel d’une requête, ou mise en file FPM/DB qui fait patienter une requête.
Correctif : Trouvez d’abord pourquoi les requêtes sont lentes (saturation FPM, verrous DB, plugin). Ensuite, définissez les timeouts volontairement : les timeouts Nginx doivent être légèrement supérieurs au pire cas réaliste pour les requêtes interactives. Pour les tâches longues, ne les exécutez pas de manière synchrone via HTTP.
3) 500 Internal Server Error juste après un déploiement/reload
Symptômes : Tout allait bien, puis reload, puis 500. Le log Nginx mentionne rewrite or internal redirection cycle ou could not build the variables_hash ou une erreur d’inclusion.
Cause racine : Boucles de réécriture, ordre d’includes cassé, ou regex invalides qui capturent plus que prévu.
Correctif : Utilisez la structure location WordPress connue, gardez les règles de réécriture minimales, et évitez les regex « astucieuses » qui essayent d’implémenter le routage WordPress. Nginx est un excellent routeur et un mauvais framework PHP.
4) 502/500 sur wp-admin seulement, page d’accueil OK
Symptômes : La page d’accueil se charge. Les pages d’administration échouent avec des erreurs de buffer/en‑tête. Le log montre upstream sent too big header.
Cause racine : En‑têtes upstream volumineux (généralement cookies) émis par des plugins ou des flux d’authentification dépassant les buffers FastCGI.
Correctif : Augmentez le buffering FastCGI pour ce server block, par exemple :
fastcgi_buffer_size et fastcgi_buffers.
Réduisez aussi l’explosion des cookies (désactivez le plugin qui écrit un roman en cookie).
5) 503 Service Unavailable sous charge, puis récupération
Symptômes : Des pics provoquent des 503, souvent accompagnés de logs « limiting requests » ou d’échecs upstream. Parfois seuls certains clients sont touchés.
Cause racine : Rate limiting trop agressif, limitation de connexions par IP (mauvais pour les clients NATés), ou effondrement de capacité upstream (FPM saturé).
Correctif : Ajustez le throttling selon la distribution réelle du trafic. Ne pénalisez pas tous les utilisateurs derrière un NAT. Si l’upstream s’effondre, corrigez la capacité et le caching d’abord ; le rate limiting doit protéger, pas throttler en permanence.
6) 502 aléatoires avec « upstream prematurely closed connection »
Symptômes : Échecs intermittents, aggravés sous charge. Les logs Nginx montrent upstream closed connection while reading response headers.
Cause racine : Worker PHP‑FPM qui plante, se fait tuer (OOM), atteint request_terminate_timeout, ou segfault à cause d’une extension.
Correctif : Vérifiez les logs noyau OOM, les logs FPM et les logs PHP. Stabilisez la mémoire, limitez le nombre de workers, retirez les extensions suspectes, et définissez des timeouts de terminaison raisonnables pour échouer vite plutôt que de planter de façon étrange.
7) 500/502 après activation d’un « microcache » ou fastcgi_cache
Symptômes : Les utilisateurs connectés voient des pages incorrectes, les actions admin échouent, parfois 5xx à cause de verrous de cache/stampede, headers étranges.
Cause racine : Mise en cache incorrecte de contenu dynamique/authentifié, mise en cache de réponses qui doivent bypasser, ou mise en cache de pages d’erreur.
Correctif : Cachez uniquement le trafic anonyme GET/HEAD, bypass pour les cookies wordpress_logged_in_ et les chemins admin, et n’enregistrez pas les 5xx. Le microcache peut être excellent ; c’est aussi un piège marketing redoutable.
8) 500 sur des URLs spécifiques avec « Primary script unknown »
Symptômes : Certaines routes PHP échouent ; le log ou le log FPM mentionne Primary script unknown.
Cause racine : Mauvaise correspondance root/fastcgi_param SCRIPT_FILENAME, souvent due au copier‑coller d’une config PHP générique qui ne correspond pas à votre arborescence.
Correctif : Utilisez les snippets FastCGI fournis par la distribution quand c’est possible et assurez‑vous que root pointe vers la racine documentaire WordPress. Confirmez le chemin réel de index.php.
Analyses approfondies : les coupables habituels Nginx + WordPress
Principes FastCGI à ne pas négliger
Nginx n’« exécute pas PHP ». Il parle FastCGI à PHP‑FPM. Cette conversation a trois modes d’échec récurrents :
- Échec de connexion (socket manquant/mauvais/permissions) → 502 instantané.
- Upstream mort en plein flux (crash, OOM, request_terminate_timeout) → 502 avec « prematurely closed connection ».
- Upstream trop lent (file d’attente, DB lente, job long) → 504.
Si vous traitez ces cas comme identiques, vous appliquerez la même correction encore et encore et vous vous demanderez pourquoi le graphe ne bouge pas.
Routage WordPress : la ligne try_files qui compte
WordPress veut des « permalinks » propres et s’attend à ce que les chemins inexistants soient routés vers index.php. L’approche Nginx propre est un try_files qui vérifie un fichier réel, puis un répertoire réel, puis délègue à /index.php?$args.
Les erreurs qui provoquent des 5xx sont presque toujours des variations de :
- Omission de
$args, qui casse le comportement des query strings et peut déclencher une logique de plugin étrange, des redirections, et (oui) des boucles dans certaines conditions. - Mauvais root de sorte que
/index.phpn’existe pas là où Nginx le pense. Nginx route vers PHP ; PHP ne trouve pas le fichier ; vous voyez des 500/502 selon la config. - Locations regex qui interceptent trop, en particulier celles qui cherchent à « sécuriser WordPress » en bloquant des motifs. Les listes de blocage sont là où de bonnes intentions deviennent des pannes.
Timeouts : alignez la chaîne, n’augmentez pas juste les chiffres
Le tuning des timeouts est l’endroit où les SRE se font reprocher d’être « trop prudents » jusqu’au jour où le serveur s’effondre lentement.
Vous avez des timeouts à plusieurs niveaux :
- Nginx :
client_header_timeout,client_body_timeout,send_timeout, plus les timeouts FastCGI commefastcgi_connect_timeout,fastcgi_send_timeout,fastcgi_read_timeout. - PHP‑FPM :
request_terminate_timeoutet (optionnellement) des limites d’exécution au niveau PHP. - PHP :
max_execution_time(différent en CLI et FPM), limites mémoire, et timeouts au niveau des extensions. - Base de données : attentes de verrou et timeouts de requêtes (ou absence de ceux‑ci).
Si Nginx abandonne à 60 secondes mais FPM maintient un worker occupé 180 secondes, vous avez créé une fuite de ressources sous charge : des requêtes restent en cours après que le client ait reçu un timeout. C’est comme ça qu’on obtient une spirale : plus de timeouts → plus de workers bloqués → plus de timeouts.
Faites plutôt :
- Définissez des budgets de requêtes interactives (ex. : les pages d’admin doivent être rapides ; les tâches en arrière‑plan asynchrones).
- Assurez‑vous que le timeout Nginx est légèrement au‑dessus du maximum attendu pour ces endpoints interactifs.
- Assurez‑vous que le timeout de terminaison FPM est légèrement au‑dessus de Nginx pour pouvoir logger les scripts lents et les nettoyer.
- Déplacez le travail long vers des queues/cron/workers CLI. WordPress peut le faire, mais pas par souhaits magiques.
Buffers : la raison cachée pour laquelle votre admin « meurt »
Les buffers FastCGI définissent combien d’en‑têtes/corps Nginx tiendra en mémoire en lisant depuis l’upstream. Quand ils sont trop petits, Nginx peut échouer à lire les en‑têtes et renvoyer un 502.
Les réponses d’admin WordPress peuvent grossir les en‑têtes à cause de :
- Cookies énormes (plusieurs plugins qui stockent état ou tracking).
- Multiples
Set-Cookiedurant les flux d’auth. - Chaînes de redirection longues ou plugins de sécurité ajoutant des en‑têtes.
La bonne démarche est un tuning ciblé avec preuve via les logs. Augmentez les buffers dans le server block qui sert WordPress, validez l’impact mémoire, et ensuite — ce que beaucoup sautent — réduisez l’encombrement des cookies. Vous n’avez pas besoin d’un cookie de la taille d’une nouvelle courte.
Permissions de fichiers et propriété : le truc ennuyeux qui cause de vraies pannes
WordPress a besoin d’un accès en lecture aux fichiers PHP et d’un accès en écriture pour les uploads, caches, et parfois mises à jour de plugins (selon votre méthode de déploiement). L’erreur la plus fréquente n’est pas « permissions incorrectes », mais « les permissions ont été changées sur un hôte lors d’une intervention manuelle urgente ».
Surveillez :
- L’utilisateur worker Nginx ne peut pas traverser les dossiers (bit d’exécution manquant).
- Le pool FPM tourne sous un autre utilisateur que prévu, et ne peut pas lire le code ou écrire les uploads.
- Le système de déploiement crée des fichiers appartenant à un utilisateur CI avec des modes restrictifs.
Corrigez avec une propriété cohérente et un modèle de déploiement qui n’exige pas que le serveur web modifie le code. Si votre production inclut « le site se met à jour tout seul », vous avez accepté le chaos opérationnel comme fonctionnalité.
HTTP/2 et concurrence : pourquoi « quelques utilisateurs » peuvent fondre PHP‑FPM
HTTP/2 permet à un client d’ouvrir de nombreux streams concurrents. C’est excellent pour charger des pages plus vite. C’est aussi une manière pour un onglet de navigateur (ou un bot) de créer une rafale de hits PHP parallèles : panels admin, endpoints API, et assets servis via PHP par erreur.
Si votre config Nginx route trop vers PHP (images via PHP, ou absence de cache pour statiques), HTTP/2 peut accélérer la douleur. La solution : servir les fichiers statiques comme statiques, agressivement et correctement, et faire en sorte que PHP ne gère que ce qui a besoin de PHP.
Quand les règles de durcissement « sécurité » causent des 5xx
WordPress attire les snippets de durcissement comme les papillons vers une lumière. Beaucoup sont corrects. Certains cassent les uploads, les endpoints REST, ou les flux admin en refusant des méthodes ou des chemins de façon incorrecte.
Modèles de casse communs :
- Bloquer
POSTvers/wp-json/ou/wp-admin/admin-ajax.phpparce que « AJAX fait peur ». Cela peut remonter en 500/503 selon la manière dont le refus est géré et ce que l’application attend. - Refuser l’accès à
/wp-content/uploads/avec des règles trop larges, provoquant des erreurs 5xx quand les plugins ne peuvent pas récupérer de ressources. - Essayer de bloquer l’exécution PHP dans uploads mais bloquer par erreur des endpoints légitimes à cause d’une mauvaise regex.
Le durcissement doit être testé comme du code applicatif. Mettez‑le en staging. Ajoutez des checks de régression pour les actions admin. Et gardez vos règles lisibles, car vous les relirez à 2 h du matin.
Blague #2 : La façon la plus rapide de découvrir que vous n’avez pas de staging est de déployer un « snippet de sécurité » directement en production et d’appeler ça du courage.
Trois mini‑histoires d’entreprise tirées du terrain
Mini‑histoire 1 : L’incident causé par une mauvaise hypothèse
Une entreprise de taille moyenne exécutait WordPress derrière Nginx sur deux nœuds applicatifs. Ils ont fait une mise à jour PHP, et le nouveau paquet utilisait un chemin de socket PHP‑FPM différent de l’ancien. La checklist de déploiement disait « redémarrer les services », et tout le monde a exécuté. Les checks de santé du load balancer étaient basiques : ils tapaient / et cherchaient un 200.
Sur un nœud, la config site Nginx pointait toujours vers l’ancien socket. Le contenu statique de la page d’accueil était mis en cache et servi correctement, donc le check de santé restait vert. Le premier utilisateur réel qui a essayé de se connecter a obtenu un 502. Puis tous les éditeurs ont eu un 502. L’équipe marketing l’a découvert en tentant de publier un billet urgent et en voyant le panneau d’admin tourner jusqu’à l’échec.
L’ingénieur on‑call a d’abord supposé « la mise à jour PHP est cassée » et a commencé à faire un rollback. Mais cela n’a pas aidé car le redémarrage a conservé le nouveau chemin de socket et le vieux mismatch de config Nginx est resté. Ce n’était pas un défaut logiciel ; c’était l’hypothèse que « PHP‑FPM vit toujours au même chemin ».
Le correctif fut douloureusement simple : pointer fastcgi_pass vers le socket correct, recharger Nginx, et ajouter un check de santé qui exerce une route PHP dynamique (quelque chose de léger comme un /health.php dédié). Ils ont aussi ajouté un garde‑fou : une vérification pré‑reload qui confirme que le socket existe et est joignable par l’utilisateur Nginx.
Ce qui a changé à long terme n’était pas le chemin du socket. C’était la culture : ils ont cessé de considérer « la page d’accueil renvoie 200 » comme définition de la santé pour une application PHP.
Mini‑histoire 2 : L’optimisation qui a mal tourné
Une autre équipe voulait « accélérer WordPress » sans acheter plus de serveurs. Ils ont activé le microcaching dans Nginx et se sont sentis héros. Le trafic anonyme était plus rapide. Les graphiques s’amélioraient. Quelqu’un a collé une capture d’écran dans un slide trimestriel.
Puis les choses étranges ont commencé. Les utilisateurs connectés voyaient parfois la mauvaise page admin. Les éditeurs se plaignaient qu’enregistrer un brouillon renvoyait parfois un 502. L’équipe support a reçu des tickets « j’ai cliqué sur publier et ça a disparu ». L’ingénieur on‑call a regardé le log Nginx et a vu un mélange de timeouts upstream et de comportements de verrou liés au cache lors des rafales.
La cause n’était pas le microcache en soi ; c’était son application trop large. Ils mettaient en cache des réponses qui ne devaient jamais l’être : pages admin, requêtes avec cookies d’auth, et certains endpoints de plugins. Sous charge, un cache stampede s’est formé : beaucoup de requêtes attendaient la même computation upstream, mais à cause de la clé de cache et des conditions de contournement, elles ne se regroupaient pas correctement.
Le correctif a inclus des règles strictes de contournement pour tout ce qui contenait des cookies d’auth WordPress, un no‑cache explicite pour admin et AJAX, et un cache conservateur uniquement pour anonymous GET/HEAD. Ils ont aussi ajouté une règle « ne pas mettre en cache les 5xx », car cacher une erreur transforme un incident transitoire en panne prolongée.
La leçon n’était pas « ne jamais cacher ». C’était : le caching est un comportement applicatif. Traitez‑le comme du code, testez‑le comme du code, et déployez‑le lentement comme si vous étiez responsable du résultat. Parce que vous l’êtes.
Mini‑histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une grande entreprise hébergeait WordPress parmi d’autres sites sur une plateforme Nginx partagée. Leurs configs étaient générées depuis des templates et commités en contrôle de version. Chaque changement nécessitait un test de config et un smoke test automatisé qui frappait : la page d’accueil, un endpoint PHP dynamique, la page de login, et un endpoint d’upload média avec un petit fichier.
Un jour, un ingénieur bien intentionné a proposé un nettoyage : « standardiser les includes FastCGI sur tous les sites ». Ils ont modifié un snippet partagé utilisé par des dizaines d’hôtes. Ça semblait sûr. Ça ne l’était pas. Le snippet a changé la façon dont SCRIPT_FILENAME était calculé, ce qui a cassé un sous‑ensemble de sites dont les chemins root n’étaient pas uniformes.
La pipeline l’a détecté. Le smoke test a échoué sur la route de login avec un 500, et les logs affichaient Primary script unknown. Personne n’a eu à apprendre ce mode de panne pendant un incident live. Le correctif fut d’ajuster le template pour respecter le root spécifique à chaque site et d’ajouter un test de type unit qui valide les chemins résolus attendus pour chaque vhost.
Ce n’était pas de l’ingénierie glamour. Personne n’a eu d’endorphine en se disant « nous avons évité une panne ». Mais ça a économisé des heures d’indisponibilité et beaucoup de crédibilité interne.
Si vous cherchez la morale : les pratiques ennuyeuses évoluent mieux que le débogage héroïque.
Listes de contrôle / plan pas-à‑pas (déployer sans drame)
Étape par étape : de l’alerte 5xx au service stable
- Confirmez le rayon d’impact. Une URL ou toutes les routes PHP ? Anonyme seulement ou admin aussi ?
- Extraites les lignes d’erreur Nginx principales. Cherchez échecs de connexion upstream, timeouts, erreurs d’en‑tête/buffer, boucles de réécriture.
- Vérifiez la santé PHP‑FPM. Statut du service, existence du socket, alignement des permissions, avertissements max_children.
- Vérifiez les OOM/redémarrages. Les logs noyau et systemd racontent souvent la vraie histoire.
- Mesurez une requête. Utilisez curl pour voir si c’est un échec instantané ou un timeout.
- Atténuez en sécurité. Scalez FPM dans les limites mémoire, bypassez un cache problématique, désactivez le plugin qui fait du travail long, ou augmentez temporairement les timeouts avec ticket de correction ultérieur.
- Vérifiez avec un endpoint de santé dynamique. Ne déclarez pas victoire sur du contenu statique.
- Notez la signature. « Si vous voyez la ligne X, c’était la cause Y. » Ceci réduit le MTTR plus que presque tout tuning.
Checklist de config : bloc serveur WordPress minimal viable
- root correct pointant vers la racine documentaire WordPress.
- try_files incluant
$args:try_files $uri $uri/ /index.php?$args; - location PHP incluant le snippet correct et le
fastcgi_passapproprié. - Refuser l’accès aux fichiers sensibles de façon réfléchie (
.htaccess, sauvegardes wp‑config), sans regex chaotiques. - Cache statique pour assets servis directement par Nginx, pas via PHP.
- Taille d’upload alignée avec les limites PHP.
- Logging incluant les timings upstream au moins pendant les fenêtres d’incident.
Checklist opérationnelle : empêcher les récurrences de 5xx
- Les checks de santé doivent frapper PHP. Un seul endpoint dynamique peut vous sauver d’une moitié de pools verts.
- Verrouillez et suivez le chemin du socket PHP‑FPM. Évitez les defaults implicites qui changent lors des upgrades.
- Cappez et observez FPM. Fixez
pm.max_childrensur la base de mesures mémoire, pas d’intuitions. - Activez les slow logs. Si vous ne savez pas ce qui est lent, vous « résoudrez » les timeouts pour toujours.
- Ne laissez pas la production se mettre à jour seule. Déployez le code comme des adultes : CI, artefacts, rollbacks.
- Testez les reloads Nginx.
nginx -tn’est pas optionnel. Vérifier que la nouvelle config est active non plus.
FAQ
1) Pourquoi j’ai un 502 au lieu d’un 504 ?
502 signifie typiquement que Nginx n’a pas pu établir ou maintenir une connexion upstream valide (socket manquant, permission refusée, upstream fermé tôt). 504 signifie que Nginx s’est connecté mais n’a pas reçu de réponse dans le temps imparti. Vos logs d’erreur indiquent généralement la différence.
2) Dois‑je utiliser un socket Unix ou TCP pour php‑fpm ?
Sur le même hôte, un socket Unix est courant et efficace, avec moins de pièces mobiles. Utilisez TCP quand vous avez besoin de connectivité inter‑conteneurs ou inter‑hôtes, ou quand l’environnement rend les permissions de socket problématiques. Dans tous les cas, gardez le choix cohérent et surveillé.
3) J’ai augmenté fastcgi_read_timeout et les 504 ont cessé. Suis‑je tranquille ?
Vous avez peut‑être juste échangé un « échec rapide » contre une « file d’attente plus longue ». Si les requêtes sont lentes à cause de la saturation FPM ou d’une DB lente, des timeouts plus élevés peuvent augmenter la pression de concurrence et aggraver les pics. Utilisez les slow logs et les métriques de queue pour confirmer que vous avez réparé la cause, pas le symptôme.
4) Qu’est‑ce qui cause « upstream sent too big header » sur WordPress ?
Généralement l’encombrement des cookies : trop de cookies, trop gros, ou trop nombreux Set-Cookie. L’admin WordPress plus des plugins est la tempête parfaite. Corrigez en augmentant les buffers FastCGI et en réduisant la croissance des cookies quand possible.
5) Les réécritures Nginx peuvent‑elles causer des 500 ?
Oui. Les boucles de réécriture et les cycles de redirection interne peuvent produire des 500. WordPress a besoin d’un try_files simple et de réécritures minimales. Si vous faites une logique de réécriture complexe, vous ré‑implémentez probablement mal le routage WordPress.
6) Comment savoir si le goulot d’étranglement est PHP‑FPM ou la base de données ?
Commencez par les slow logs PHP‑FPM. Si les traces montrent des appels DB (par ex. dans wp-db.php), la base est probablement le facteur limitant. Regardez aussi les avertissements pm.max_children (file d’attente) et corrélez avec les métriques DB (attentes de verrou, requêtes lentes). Un 504 est souvent « quelqu’un a attendu autre chose ».
7) Pourquoi seuls wp-admin et wp-login.php échouent alors que la page d’accueil marche ?
Les pages admin génèrent souvent des en‑têtes plus gros et dépendent davantage des cookies. Elles sont aussi plus dynamiques, donc elles exposent les problèmes upstream plus tôt. Si la page d’accueil est mise en cache ou majoritairement statique, elle peut masquer les défaillances upstream. C’est pourquoi les checks de santé statiques mentent.
8) Est‑ce sûr d’activer fastcgi_cache pour WordPress ?
Cela peut être sûr pour le trafic anonyme si vous bypasser le cache pour les cookies de connexion, les chemins admin, les URLs de preview, les paniers/checkout et tout ce qui est personnalisé. Un cache mal appliqué casse d’abord la correction fonctionnelle, puis la disponibilité. Testez soigneusement et déployez progressivement.
9) Quelle est la cause simple la plus commune d’un 502 après maintenance ?
Un chemin de socket mismatch après une mise à jour PHP ou un service redémarré qui a recréé le socket avec des permissions différentes. C’est embarrassant et fréquent, et rapide à détecter en vérifiant fastcgi_pass et en listant le socket avec ls -l.
10) Dois‑je tuner worker_processes et worker_connections Nginx pour les 5xx WordPress ?
Parfois, mais rarement comme première action. Les pannes WordPress sont souvent dues au CPU/RAM/DB upstream, pas à une pénurie de workers Nginx. Cependant, si vous voyez des limites de descripteurs ou des caps de connexion, corrigez‑les. Nginx est souvent le messager, pas le meurtrier.
Conclusion : étapes suivantes pour éviter les reprises
Si vous voyez des 5xx sur WordPress derrière Nginx, arrêtez de l’interpréter comme un caprice mystique du serveur web. Les modes d’échec sont constants : connectivité socket, capacité upstream, timeouts, buffers, et routage. Les logs vous diront lequel, si vous les lisez comme un opérateur et non comme un voyant.
Étapes pratiques suivantes :
- Ajoutez un endpoint de santé dynamique et faites en sorte que votre load balancer le vérifie.
- Standardisez et vérifiez les chemins et permissions des sockets lors des déploiements et upgrades.
- Activez le slow logging PHP‑FPM et traitez « lent » comme un bug de fiabilité.
- Tunez le nombre de workers FPM basé sur la mémoire mesurée, et arrêtez quand vous atteignez une marge de sécurité.
- Rendez le caching explicite et conservateur : uniquement GET/HEAD anonymes sauf raison solide.
- Rédigez un runbook court en utilisant les tâches ci‑dessus, pour que le prochain incident soit une procédure et non un débat.
Faites cela, et votre prochain 502 ne sera plus un mystère. Ce sera une classe de problèmes connue avec une courte liste de correctifs — et c’est à cela que ressemble la « fiabilité » dans la vraie vie.