A white page that says "502 Bad Gateway" means the web server is up but did not get a proper answer from the service behind it, and that clue alone is half the troubleshooting. In this guide we first explain in plain terms what this error (and its siblings, 503 and 504) actually means, then work through a practical diagnostic funnel to fix 502 Bad Gateway step by step: from restarting PHP-FPM and reading the Nginx error log to catching the OOM killer and working out whether the problem lies with Cloudflare or with your origin server.
What does a 502 Bad Gateway error mean, in plain terms?
Most modern websites have two layers. Out front sits a web server or proxy (usually Nginx, sometimes Cloudflare or HAProxy), and behind it a "backend" does the real work: PHP-FPM for WordPress, a Node.js app, or Gunicorn for Django. When the proxy forwards your request to the backend and gets nothing back, has its connection refused, or receives a broken or incomplete response, it goes back to the browser empty-handed and says 502 Bad Gateway: "I am the gateway, but whatever is on the other side of me did not give a proper answer."
So here is the key point: in a 502, the web server is almost always fine and the backend is the one in trouble. It is dead, restarting, out of RAM, or Nginx simply has the wrong address for it. Let's also tell apart the three errors of this family:
| Code | Meaning | Usual culprit |
|---|---|---|
502 Bad Gateway | The proxy reached the backend but got no valid response (or the connection was refused outright) | Crashed backend, wrong socket, not enough RAM |
503 Service Unavailable | The service is "temporarily unavailable", on purpose or under heavy load | Maintenance mode, exhausted resources, shared-hosting limits |
504 Gateway Timeout | The backend is alive, but it took so long to answer that the proxy gave up | Heavy query, slow script, short timeout |
The distinction matters because the fixes differ: a 502 means "bring the backend back to life", a 504 means "make the backend faster or give it more time", and a 503 often means "you have run out of capacity". If repeated 503s on shared hosting are what keeps biting you, that is a separate story, which we tell in full in Shared hosting or a VPS?
If you are just a visitor, do these four things
In more than 90 percent of cases a 502 is a problem with the site itself, not with your internet connection. But before you decide:
- Refresh once. If the backend was restarting (in the middle of a deploy, say), the site will be back within a few seconds. A hard refresh with
Ctrl+F5also bypasses the browser cache. - Try another network. Open the site over mobile data. If it loads there, the problem is your DNS or your network path, not the site.
- Flush your DNS cache. On Windows, run
ipconfig /flushdnsin PowerShell. Your cache may still hold the old IP address of a server that has since gone out of service. - Wait a few minutes. A 502 is usually temporary; the site's administrator is probably restarting their server right now.
If the error is still there after all that, the site really is down, and if that site is yours, the rest of this article was written precisely for you.
If you run the site: fix 502 Bad Gateway with a top-down funnel
Troubleshooting a 502 is not guesswork. It is a fixed path you follow every time, from the quickest check to the deepest:
systemctl status php8.3-fpm (or your backend service): is it alive? 2) tail -n 30 /var/log/nginx/error.log: what is the exact message? 3) free -h: has RAM run out? 4) dmesg | grep -i kill: has anything been OOM-killed? 5) Have you deployed or updated anything recently? Make that your prime suspect. We unpack each item below.And if SSH will not come up either (the server is completely locked up), the web console in the Mehran Host panel is your way back in: you reach the server straight from your browser and restart it, without depending on SSH at all.
Step one: the Nginx error log tells the truth
Never restart blindly. First see what Nginx itself has to say:
sudo tail -n 30 /var/log/nginx/error.log
Three messages close the case on most 502s:
connect() failed (111: Connection refused) while connecting to upstream
Nginx knocked, but nobody was home: the backend (PHP-FPM or the Node app) is dead, or it is not listening on that port or socket. Go straight to the next section.
connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory)
Nginx is pointing at a socket that does not exist. The classic case comes right after a PHP upgrade: the config still calls php8.2, while only php8.3-fpm is installed on the server.
upstream sent too big header while reading response header from upstream
The backend is alive, but its response header (often the heavy cookies and redirects of WordPress or its security plugins) is bigger than Nginx's buffer. The fix goes in the location block that handles PHP:
fastcgi_buffer_size 32k;
fastcgi_buffers 16 32k;
# and for a plain reverse proxy:
proxy_buffer_size 32k;
proxy_buffers 16 32k;
After every config change: sudo nginx -t && sudo systemctl reload nginx.
The most common culprit: PHP-FPM is down
On WordPress servers and on most PHP sites, a 502 is practically a synonym for "PHP-FPM is not running". Check its status:
systemctl status php8.3-fpm
# if it is down:
sudo systemctl restart php8.3-fpm
# and make sure it also starts on its own after a reboot:
sudo systemctl enable php8.3-fpm
If the service will not start, read the reason in the journal. It is usually a syntax error in a pool config or a broken extension:
journalctl -u php8.3-fpm -n 30 --no-pager
Socket path mismatch: the silent killer after an upgrade
Nginx and PHP-FPM talk over a Unix socket, and the socket's name has to be the same on both sides. Compare the two:
# Where is Nginx sending requests?
grep -r fastcgi_pass /etc/nginx/sites-enabled/
# Where is PHP-FPM actually listening?
grep "^listen" /etc/php/8.3/fpm/pool.d/www.conf
ls /run/php/
On Ubuntu the correct path is unix:/run/php/php8.3-fpm.sock. AlmaLinux 9 is a different story: the service is simply called php-fpm, and its default socket is /run/php-fpm/www.sock. If the config was copied from another distribution, this one small mismatch means a permanent 502.
grep -r "php8" /etc/nginx/ and bring every reference up to the new version. Half of all "it broke after last night's update" 502s are solved right here. We walk through setting up this pair cleanly, step by step, in our guide to Nginx on Ubuntu.A Node or Gunicorn backend behind a reverse proxy
If Nginx connects to a Node app, Gunicorn or a Docker container with proxy_pass http://127.0.0.1:3000;, first check whether anything is listening on that port at all:
ss -tlnp | grep 3000
Empty output means the app has crashed. Look at its log (journalctl -u myapp -n 50 for a systemd service, pm2 logs for PM2, or docker logs if it runs in a container; see our guide to Docker on a cloud server). The three most frequent reasons an app crashes after a deploy are a missing environment variable, a port that is already taken (EADDRINUSE in the Node log) and incompatible dependency versions. One subtle point: if the app listens only on localhost inside the container while Nginx runs outside it, the connection is refused. The app has to listen on 0.0.0.0, or its port has to be mapped correctly.
Out of RAM? Track down the OOM killer
When a server runs out of RAM, the Linux kernel kills the process using the most memory in order to survive, and that process is often MySQL or PHP-FPM. The result: the site is fine for a few hours, then suddenly returns a 502, a restart fixes it, and the next day it happens again. This "periodic 502" pattern is the OOM killer's signature. Find the evidence:
dmesg | grep -i "killed process"
# or:
journalctl -k | grep -i oom
If you see a line like this one, the case is closed:
Out of memory: Killed process 1471 (mysqld) total-vm:1834244kB
Check the current state of RAM as well (we explain the output of free, and the other monitoring tools, in full in our Linux server monitoring guide):
free -h
The short-term fix: restart the service that was killed and create a 2 GB swap file as a shock absorber:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile && sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
The real fix, though, is one of two things: cut usage (proper caching, capping pm.max_children, tuning MySQL) or add RAM. The advantage of a cloud server is that a RAM upgrade takes a few clicks, not a migration. Just be aware that upgrades are one-way and resources cannot be scaled back down later, so go one step up, not three.
PHP-FPM limits: when the queue fills up
PHP-FPM has a limited number of workers (pm.max_children). When all of them are busy, new requests wait in a queue, and if the queue overflows too, or the workers crash one after another, Nginx serves a 502 or a 504. The sign shows up in FPM's own log:
sudo tail -n 20 /var/log/php8.3-fpm.log
[pool www] server reached pm.max_children setting (5), consider raising it
Work out the right number from your free RAM. A WordPress worker typically uses 60 to 90 MB, so on a server with about 2 GB of free RAM, pm.max_children = 20 is a sensible figure. In /etc/php/8.3/fpm/pool.d/www.conf:
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
Then run sudo systemctl reload php8.3-fpm. Raising this number without doing the math causes OOM kills of its own, the same vicious circle as in the previous section. If your WordPress site keeps hitting this ceiling, turn on page caching and OPcache before you buy more RAM: with good caching, most visits never reach PHP at all.
Timeouts: when a 502 is really a 504
If the error appears after a long pause (exactly 60 seconds, for example), you are dealing with a timeout, not a crash. By default, Nginx waits 60 seconds for the backend to answer. For operations that are slow by nature (a large WooCommerce import, a backup run from inside WordPress, a heavy report), raise this limit deliberately, and only in that one block:
# behind fastcgi (PHP):
fastcgi_read_timeout 300;
# behind a reverse proxy (Node/Gunicorn):
proxy_read_timeout 300;
proxy_connect_timeout 10;
Keep in mind that PHP has its own limit as well (max_execution_time in php.ini). If that stays at 30 seconds, PHP stops the job itself before Nginx's timeout is reached. And one principle: a 300-second timeout is not a cure but a painkiller. A request that takes 5 minutes should either be optimized or moved to a background queue (cron or a worker).
A 502 after an update or deploy: look back
If the site was fine until last night and returns a 502 today, the first question is not "what is broken?" but "what changed?" The usual suspects:
- A PHP upgrade. The socket and the service name have changed, but the Nginx config is still the old one (see the socket section above); or an extension (such as
php8.3-mysql) was not installed after the upgrade. - A new code deploy. The Node or Python app crashes on startup. Read the startup log, not just the Nginx log.
- A nightly
apt upgrade. A service was restarted and did not come back up.systemctl --failedlists the services that have gone down. - An Nginx config change. Always validate with
nginx -tbefore a reload: a broken config can leave the reload with no effect, while you assume your change has been applied.
A 502 behind Cloudflare: whose problem is it?
When a site sits behind Cloudflare, the error page itself tells you where the fault lies. If the bottom of the page says cloudflare and the "Host" box is marked with an error, Cloudflare is fine and your origin server did not answer, so run every check in this article on your own server. To be sure, bypass Cloudflare and test the origin directly:
curl -I --resolve example.com:443:203.0.113.10 https://example.com/
(Replace 203.0.113.10 with your server's real IP address.) If you get a 502 directly as well, the problem is definitely inside the server. If the direct test is fine but you still get the error through Cloudflare, look at two things: a server firewall that is blocking Cloudflare's IP addresses, or an SSL mismatch. The encryption mode in Cloudflare should be Full (strict), with a valid certificate installed on the origin; a mismatch here usually shows up as an error from the 52x family (such as 521 or 526). If you have only just moved your DNS to Cloudflare and are seeing strange behavior, a look at our guide to DNS records is time well spent.
Next step
The next time you see a 502, you won't restart in a panic: you will read the Nginx log, check the backend and the RAM, and reach the root cause in a few minutes. If you want to practice these scenarios without putting your real site at risk (kill PHP-FPM on purpose, break the socket, then fix it), create an hourly cloud server and delete it when you are done. Hourly billing continues until the moment you delete it, and powering the server off does not stop it, because its resources stay reserved. And to visit this page less often in the first place, setting up Nginx properly is a good place to start.
Frequently asked questions
How do I fix a 502 Bad Gateway error, and where do I start?
To fix a 502 Bad Gateway, read the web server's error log first and only then turn to the backend service, never the other way round. The path has four fixed steps: tail -n 30 /var/log/nginx/error.log for the exact message, systemctl status on PHP-FPM or the Node app to confirm it is alive, free -h to check RAM, and finally the question of what changed on the server since last night. Usually that one log line names the culprit; a blind restart only hides the cause.
Why is my WordPress site showing a 502 error?
On WordPress, a 502 almost always means PHP-FPM is down or out of capacity. Three scenarios are common: the FPM service has crashed and comes back with systemctl restart; after a PHP version upgrade, the web server config still calls the old version's socket; or the workers have hit the pm.max_children ceiling and the queue has overflowed. A fourth, lesser-known cause is an oversized response header from security plugins, which you fix by raising fastcgi_buffer_size.
What is the difference between 502, 503 and 504 errors?
All three are server-side errors, but they describe different situations. In a 502, the proxy reached the backend but got no valid response or was refused; in a 504, the backend is alive but answered after the proxy's deadline; in a 503, the service says it is temporarily unavailable, because of maintenance or exhausted resources. The fixes differ too: revive the backend for a 502, optimize the code or raise the timeout selectively for a 504, and add capacity for a 503.
Why does a 502 error keep coming back and then fix itself?
A site that is fine for a few hours, then throws a 502, then recovers almost always has a RAM shortage or a full PHP worker pool. When memory runs out, the Linux kernel kills the most memory-hungry process, usually MySQL or PHP-FPM, and systemd brings that service back shortly afterwards, so the site seems to heal itself. Find the evidence with dmesg | grep -i "killed process". Until you cut usage or add RAM, the same cycle will repeat tomorrow.
On shared hosting without SSH access, what can I do about a 502 error?
On shared hosting you can do three things: roll back the last change, reduce the load, and then open a ticket. In practice, disable the plugin or theme you just installed or updated by renaming its folder through the host's file manager or FTP, read the error_log file next to the site's files, and turn on page caching. The PHP service restart and the server's RAM are outside your account's control; only the provider can touch them.