Troubleshooting guide · wordpress · Published 2026-08-15 · 4 min read
WordPress site returns 502 bad gateway
Fix a WordPress 502 bad gateway: check PHP-FPM worker health, fastcgi logs, worker and timeout settings, then verify via WP-CLI.
- ·Why 502
- ·Worker check
- ·Timeout fix
Symptoms
The site answers 502 Bad Gateway from nginx with a plain page, or a host-branded gateway page. The front end and admin are usually both down, because the admin runs the same PHP-FPM pool. If a 522 appears from another network, that is the edge-to-origin variant in the Cloudflare family, while a 502 means the origin answered in an unusable way. The 502 bad gateway article covers the proxy-level view; here the fixes are PHP-FPM and WordPress specific.
Common causes
- A PHP-FPM worker crashed and nobody restarted it. nginx forwards to the pool, gets nothing back, and 502s.
- One hung plugin or theme swallows the worker: the request blocks forever waiting on a lock, the process is killed, and the pool drains until every new request 502s.
- Slow cron or a heavy admin export runs in
wp-adminand the worker sets free only through timeout, so during busy periods the pool is out. pm.max_childrenset too low for the traffic level; the pool queues, the proxy times out waiting, 502.pm.max_requestsnever set, so long-lived workers leak memory until one is killed mid-request.- Timeouts shorter than the work: the plugin loads images, calls a remote API, or runs an import longer than
fastcgi_read_timeout.
Fix in order
- Confirm the 502 is live, not cached. A CDN or cache layer can serve a stale 502 for a while after PHP recovers. Test
curl -sIfrom a shell outside the CDN; then look at nginx logs:
tail -n 50 /var/log/nginx/error.log
Find the exact line upstream timed out or connect() to ... 9000 failed. The message names the socket path and often the worker.
- Restart PHP-FPM first. The proxy restart alone can make it worse: nginx comes up, floods a dead pool, and worsens the 502 string. Restart the pool in the order that heals fastest:
systemctl restart php-fpm
systemctl restart nginx
If the restart clears it, the worker had wedged. Now find out why before it happens again.
- Read the WordPress log for the underlying fatal. A plugin or theme process dying with a fatal error exits the worker, which on a busy site looks identical to a hang. Enable the log as in white screen of death and read
wp-content/debug.logplustailthe PHP-FPM error log together; asegfault,Unable to allocate memory, or a fatal is the real cause. - Size the pool to the site. In the PHP-FPM pool config:
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 500
A safe starting rule: max_children = (memory available for PHP) / (typical peak per worker). If each worker peaks around 128 MB and you want room for 20 concurrent requests, budget roughly 2.5 GB. max_requests restarts every worker before its memory leaks grow, so set a live value such as 500.
- Raise the fastcgi timeouts for your real work.
location ~ \.php$ {
fastcgi_read_timeout 300;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
}
Only raise them to the duration your slowest legitimate request needs. An import polled for 10 minutes will 502 at 60 seconds no matter the pool health, because the timeout kills the worker before the job.
- Test through WP-CLI. WP-CLI runs outside PHP-FPM, so
wp option get homeworking from a shell proves files, config, and database are fine and the fault is the pool or proxy. If WP-CLI fails with the same error, the runtime itself is the problem (module missing, PHP version too new). - Verify the rescue: load the site and the admin in a private window, then run a health loop.
| Test | Command | What it proves |
|---|---|---|
| Pool status | curl -sI https://example.com/wp-login.php | 200 not 502 |
| PHP-FPM status | systemctl is-active php-fpm | running |
| CLI path | wp option get home --allow-root | config + DB fine |
| Log tail | tail -n 20 /var/log/php-fpm/error.log | story after the fix |
Prevention
- Keep
pm.max_children+max_requestsdocumented per site; a re-provisioned server reproduces the pool in the deploy doc. - Put a rare, slow job in
wp evalor a real system cron, notwp-admin, so no browser session holds a worker for 5 minutes. - Watch the pool: if the log grows 'queue full' lines, raise children only after analyzing peak per worker, not as the default move.
When to involve a professional
A 502 at every page even after a clean pool restart, with WP-CLI working, points to a proxy misconfiguration: wrong socket path, a FastCGI module missing, or a firewall dropping the upstream port. That is a host-level fix best done by the host, not by editing the website.