Why 502 is harder than 500
A 500 tells you something inside WordPress broke. A 502 tells you the path between visitor and WordPress broke — your proxy (Nginx, Cloudflare, load balancer) tried to talk to the WordPress upstream and got nothing back, or got something it couldn't parse.
Because the failure is at the edge between two systems, debugging requires looking at both. This article is the upstream-by-upstream playbook we use on every 502.
The architecture map
Most modern WordPress sites have this request path:
Visitor → Cloudflare → Nginx → PHP-FPM → MySQLA 502 means one of those arrows broke. Identify which arrow first, then dig into that specific layer. Don't restart PHP-FPM just because someone said it might help.
Cause 1 — PHP-FPM out of workers
The most common upstream cause. PHP-FPM's worker pool is saturated. New requests queue, eventually time out, Nginx returns 502.
Detect
# Nginx log
tail /var/log/nginx/error.log | grep "upstream prematurely closed"
# PHP-FPM status
systemctl status php-fpm
journalctl -u php-fpm -n 50 | grep "max_children"If you see server reached pm.max_children setting, this is your cause.
Fix
Calculate the right value (see our 500 deep dive for the formula), set in /etc/php/8.1/fpm/pool.d/www.conf:
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500Reload PHP-FPM:
systemctl reload php-fpmCause 2 — Single slow request blocking workers
You don't have many requests — just one really slow one (an admin export, a heavy import, a sitemap rebuild) that holds workers for 60+ seconds. Other requests pile up behind it, get rejected, become 502s.
Detect
Look at PHP-FPM slow log (enable if not on):
slowlog = /var/log/php-fpm/www.slow.log
request_slowlog_timeout = 10sReload PHP-FPM, reproduce, examine /var/log/php-fpm/www.slow.log.
Fix
- Move the slow operation to a background job (Action Scheduler, WP-CLI cron)
- Use a separate PHP-FPM pool for admin requests so they can't starve the front-end pool
- Add a "long-running" route to a separate worker pool with more memory and time
Cause 3 — Wrong fastcgi_pass after PHP version upgrade
You upgraded PHP from 7.4 to 8.1. The socket path changed (/var/run/php/php8.1-fpm.sock instead of /var/run/php/php7.4-fpm.sock). Nginx still references the old socket, which no longer exists.
Detect
grep fastcgi_pass /etc/nginx/sites-available/*
ls -la /var/run/php/If the socket Nginx is pointing to doesn't exist, you found it.
Fix
Update Nginx config:
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
# ... other directives
}Reload Nginx:
nginx -t && systemctl reload nginxCause 4 — Cloudflare → origin TLS handshake failing
Cloudflare's SSL mode is "Full (strict)" but your origin certificate is self-signed or expired. Cloudflare refuses to connect; visitors see Cloudflare's branded 502.
Detect
echo | openssl s_client -connect yoursite.com:443 -servername yoursite.com 2>/dev/null | openssl x509 -noout -datesCheck notAfter date. If it's past, certificate expired.
Fix
- Renew the origin certificate (Let's Encrypt:
certbot renew) - Or switch Cloudflare SSL mode to "Full" (less strict, accepts self-signed)
- Or use Cloudflare's free Origin CA certificates (15-year validity)
Cause 5 — PHP segfault
Rare but possible. A buggy PHP extension or PHP build crashes mid-request. The worker dies; Nginx gets an incomplete response and returns 502.
Detect
journalctl -u php-fpm | grep -i "segfault\|core dumped"
dmesg | grep php-fpmIf you see segfault messages, this is the cause.
Fix
- Identify the failing extension (usually a custom PECL extension)
- Disable it:
phpdismod xdebugor similar - Update PHP to the latest patch version
- File a bug report with the extension maintainer
Workaround until fixed:
pm.max_requests = 50Forces worker recycling every 50 requests, limiting impact of slow leaks.
Cause 6 — Memory pressure causing OOM kills
The OS is killing processes because of memory pressure. PHP-FPM workers get killed mid-request. Visitors see 502.
Detect
dmesg | grep -i "out of memory\|killed process"
journalctl -k | grep -i oomFix
- Identify the memory hog (often MySQL or a runaway PHP script)
- Add swap (temporary):
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab- Long term: add RAM or split services across multiple servers
The 5-minute 502 diagnostic
# Step 1 — confirm where the 502 originates
curl -I https://yoursite.com/ # check the Server header
# Step 2 — Nginx upstream error log
tail -100 /var/log/nginx/error.log
# Step 3 — PHP-FPM worker pool status
ss -xl | grep php-fpm
curl http://localhost/fpm-status 2>/dev/null
# Step 4 — Recent memory/process events
dmesg --since "5 min ago" | tail -50
journalctl --since "5 min ago" -u php-fpm
# Step 5 — Cloudflare or origin?
echo | openssl s_client -connect yoursite.com:443 -servername yoursite.com 2>/dev/null | grep "verify return"Each step narrows the cause. Five minutes total, root cause identified.
Common mistakes when diagnosing 502
- Restarting PHP-FPM without checking what was happening — masks symptom, doesn't address cause
- Raising `pm.max_children` to a huge number — eventually exhausts RAM, makes it worse
- Blaming "the hosting" — the hosting often has nothing to do with it; your stack does
- Increasing Nginx `proxy_read_timeout` to 600s — masks slow PHP, doesn't fix anything
When to call a specialist
If you've worked through the diagnostic and the 502 returns within hours, the issue is structural — a slow plugin holding workers, a memory leak in custom code, an architecture mismatch between PHP-FPM and your traffic pattern. We handle these every week.
Emergency 502 support within minutes. For broader emergencies see WordPress emergency support.

