A 502 Bad Gateway error looks a lot like a 500 at first glance, but it means something different: your web server (usually Nginx, or a load balancer in front of it) reached out to a backend to get a response and got garbage, a dropped connection, or nothing at all. If you're running WordPress on plain Apache/cPanel shared hosting, you'll rarely see this one. If you're on a VPS with Nginx sitting in front of PHP-FPM, Node.js, or another app server, it's one of the most common errors you'll deal with. Here's how to actually track it down instead of just restarting services and hoping.
Symptom: What a 502 Actually Looks Like
The exact wording depends on what's serving the error page, but you'll usually see one of these:
- A plain "502 Bad Gateway" page with "nginx" printed at the bottom
- Cloudflare's orange error screen: "502 Bad Gateway — Web server is returning an unknown error"
- The page loads fine for a while, then starts throwing 502s intermittently, especially under load
- 502s that clear up if you refresh a few times, then come back
That intermittent pattern is a big clue. A 502 that happens on every single request usually means the backend is down. A 502 that comes and goes usually means the backend is overwhelmed, crashing under specific requests, or timing out.
Cause: Why Nginx Can't Reach Its Backend
Nginx (or whatever's acting as the reverse proxy) is just the messenger here — the actual problem is almost always downstream. Common causes, roughly in order of how often we see them:
| Cause | What's happening |
|---|---|
| PHP-FPM crashed or isn't running | The FPM pool died from an out-of-memory kill, a config reload gone wrong, or it never started after a reboot |
| PHP-FPM pool exhausted | All worker processes are busy with slow requests, so new requests queue up and time out before FPM can respond |
| Backend timeout | A slow database query, external API call, or heavy PHP script takes longer than Nginx's proxy_read_timeout or FPM's request_terminate_timeout |
| Node.js/app server crash | An unhandled exception kills the process, and nothing is left listening on the port Nginx proxies to |
| Wrong upstream address or port | Nginx config points at 127.0.0.1:9000 but PHP-FPM is actually listening on a Unix socket, or vice versa, often after a PHP version change |
| Server ran out of memory | The OOM killer takes out PHP-FPM or the app process to free RAM, leaving Nginx with nothing to talk to |
| Firewall or SELinux blocking the connection | Nginx and the backend are on different hosts or containers, and a firewall rule silently drops the connection between them |
Fix: Work From the Logs, Not Guesswork
1. Check if the backend is actually running
If you're proxying to PHP-FPM, check its status first:
systemctl status php8.1-fpm
If it's not active, start it and check journalctl -u php8.1-fpm --since "10 min ago" for why it stopped. For Node.js apps run under PM2 or a systemd unit, the equivalent is pm2 list or systemctl status your-app. If the process isn't there, that's your 502 — full stop. Everything else is figuring out why it isn't there.
2. Read the Nginx error log — it tells you exactly what failed
tail -100 /var/log/nginx/error.log
Look for lines like:
connect() failed (111: Connection refused)— nothing is listening on the address Nginx is trying to proxy to. Check yourupstreamorfastcgi_passdirective against what's actually running.upstream timed out (110: Connection timed out)— the backend was reachable but too slow to respond in time. This points to slow PHP/DB, not a crash.recv() failed (104: Connection reset by peer)— the backend process died mid-response, often an OOM kill or an unhandled crash.
3. Match the socket or port on both sides
This one bites people constantly after a PHP version upgrade in cPanel's MultiPHP Manager or a fresh Nginx install. Check what FPM is actually listening on:
grep -E "^listen" /etc/php/8.1/fpm/pool.d/www.conf
Then confirm your Nginx server block's fastcgi_pass points at the exact same socket path or 127.0.0.1:port. A mismatch here — say, FPM on a Unix socket but Nginx still configured for TCP port 9000 from an older setup — throws a 502 on every request, with no intermittency at all.
4. If it's timeout-related, find the slow thing
Check MySQL for slow queries (SHOW FULL PROCESSLIST; while the error is happening) and check whether a plugin, API integration, or cron-triggered script is holding a PHP worker open past its limit. Bumping timeouts papers over this temporarily:
fastcgi_read_timeout 300;
proxy_read_timeout 300;
Add those inside the relevant location block and reload Nginx — but treat it as a stopgap, not a fix. If a request genuinely needs 5 minutes, that's a background job, not a page load.
5. Check memory before you chase anything else
free -h
dmesg | grep -i "out of memory"
If dmesg shows the OOM killer took out php-fpm or node, the 502 is a memory problem wearing a gateway-error costume. Undersized VPS plans running WordPress plus a handful of FPM child processes are the classic case — either add swap as a buffer, reduce pm.max_children in your FPM pool, or move up a plan.
Prevention
- Set FPM to restart on failure. Add
Restart=on-failureto the systemd unit (or confirm it's already there) so a crash doesn't leave Nginx stranded until someone notices. - Monitor the process, not just the site. A uptime check that hits your homepage won't catch "FPM restarted 40 times today" — watch process health directly if you can.
- Size
pm.max_childrento your actual RAM, not a copy-pasted default. Too high and a traffic spike OOMs the box; too low and requests queue up and time out under normal load. - Keep upstream configs in version control. When you change PHP versions or move an app to a new port, the Nginx config and the backend config are two files that both need updating — track them together so they don't drift apart.
- Add swap on smaller VPS plans. It won't fix an undersized server long-term, but it buys you time instead of an instant OOM kill during a traffic spike.
If you're on a SkyServer VPS and want a second pair of eyes on your Nginx/PHP-FPM config or memory sizing, our support team can pull the logs with you and sanity-check the pool settings against your plan's RAM.
Frequently Asked Questions
Is a 502 error my fault or the server's fault?
Neither, really — it's a communication breakdown between two things you control: the proxy (Nginx) and the backend (PHP-FPM, Node, etc.). The fix is almost always a config mismatch, a crashed process, or a resource limit, all of which are fixable from your side without contacting your registrar or a third party.
Why does refreshing sometimes make the 502 go away?
If FPM or your app server is restarting automatically after a crash, or workers are freeing up after a timeout, a retry a few seconds later can succeed even though the underlying problem — the crash or the overload — hasn't actually been fixed.
Does Cloudflare cause 502 errors, or just show them?
Cloudflare is just displaying an error your origin server returned. If you see Cloudflare's orange 502 page, the actual problem is on your server — Nginx couldn't reach its backend — not with Cloudflare's network. Checking your own Nginx error log is still the right first step.
How is a 502 different from a 504 Gateway Timeout?
A 502 means the backend responded with something invalid, or the connection was refused or reset. A 504 means Nginx never got a response at all within its timeout window. In practice they share a lot of the same causes — a slow or crashed backend — and the fixes overlap heavily.
Can too much traffic alone cause 502 errors?
Yes. If PHP-FPM's pm.max_children is maxed out, new requests can't get a worker and either queue past the timeout or get refused outright, both of which show up to visitors as a 502. Raising max_children only helps if you actually have the RAM to back it — otherwise you're trading a 502 for an OOM kill.
