502 Bad Gateway in Nginx: How to Diagnose and Fix It
You reload the page and Nginx hands you a blunt little message: 502 Bad Gateway. No stack trace, no clue, just a number. The instinct is to restart Nginx and hope. Resist it. In almost every case I’ve troubleshooted, restarting Nginx fixes nothing because Nginx was never the broken part.
Here’s the mental model that solves a 502 fast: Nginx is sitting in front of your real application as a reverse proxy (a gateway). When a request comes in, Nginx passes it upstream to something else — PHP-FPM, a Node or Python app, another web server. If that upstream returns an invalid response or no response at all, Nginx can’t make sense of it, so it reports a 502 to the visitor. The 502 is Nginx telling you, honestly, “I asked the backend and it failed me.”
So the question is never “why is Nginx broken?” It’s “what did the backend do?” Let’s find out.
Key Takeaways
• A 502 Bad Gateway means Nginx, acting as a reverse proxy, got an invalid or empty response from the upstream server it proxies to.
• The fault is almost always in the backend (PHP-FPM, your app server), not in Nginx itself.
• Read `/var/log/nginx/error.log` first — it names the exact upstream and the exact failure reason.
• The usual culprits: backend is down, wrong socket/port in the config, the backend is too slow (timeout), or it ran out of memory.
• Fix the backend — don’t just restart Nginx and pray.
What does a 502 Bad Gateway in Nginx actually mean?
Think of Nginx as a receptionist who takes your request and walks it to a back office worker (the upstream). A 502 means the receptionist went to the back office and either nobody was there, the door was locked, or the worker handed back something unreadable.
That distinguishes 502 from its neighbours:
- 502 Bad Gateway — Nginx reached out to the upstream and got an invalid or no response. The backend exists in config but failed.
- 504 Gateway Timeout — Nginx connected, but the upstream took too long to answer.
- 503 Service Unavailable — usually Nginx itself (or the app) deliberately refusing, often during overload or maintenance.
If you see X (a 502), the cause is usually Y: the process Nginx proxies to is down, misconfigured, slow, or starved of resources. Keep that sentence in your head for the rest of this post.
Where do you look first when Nginx throws a 502?
The error log. Always the error log. This is the single highest-value step and most people skip it.
“`bash tail -n 50 /var/log/nginx/error.log “`
Nginx does not hide the cause — it names the upstream and the failure reason. You’ll typically see one of these patterns, and each points to a different root cause:
“` *42 connect() failed (111: Connection refused) while connecting to upstream, upstream: “fastcgi://unix:/run/php/php8.2-fpm.sock”
*88 upstream prematurely closed connection while reading response header from upstream, upstream: “http://127.0.0.1:3000”
*15 no live upstreams while connecting to upstream
*60 upstream sent too big header while reading response header from upstream “`
Read the message literally. “connect() failed (Connection refused)” means the backend isn’t listening — it’s down or the socket path is wrong. “upstream prematurely closed connection” means the backend accepted the request but died mid-response — often a crash or an out-of-memory kill. “no live upstreams” means every backend in the pool is marked dead. Each line is a signpost. Follow it before you touch anything.
A 502 from Nginx is almost never Nginx’s fault. Nginx is simply reporting that the backend it proxies to failed to respond properly. Restarting Nginx clears nothing — the broken process is still broken. The fastest fix is always the same loop: read `/var/log/nginx/error.log`, identify the named upstream failure, then fix that exact backend — restart PHP-FPM, correct the socket path, raise the timeout, or add memory. The log already told you which one. You just have to read it.
What are the common causes of a 502 in Nginx — and how do you fix each?
Here is the table I keep close when a 502 hits. Match the log line to the cause, apply the fix.
| Cause | What you see in the log | Fix |
|---|---|---|
| Backend is down / crashed (PHP-FPM, Node, Python) | `connect() failed (Connection refused)` | Check it’s running, restart it: `systemctl status php8.2-fpm` then `systemctl restart php8.2-fpm` |
| Wrong socket path or port in Nginx config | `connect() failed` to a socket/port that doesn’t exist | Correct `fastcgi_pass` or `proxy_pass` to match the backend’s real address |
| Upstream too slow (exceeds read timeout) | `upstream timed out` / often surfaces as 504, can cascade to 502 | Raise `fastcgi_read_timeout` / `proxy_read_timeout`, or fix the slow query |
| Backend out of memory / OOM-killed | `upstream prematurely closed connection` | Check `dmesg` for OOM-killer, add RAM or lower memory use |
| FPM out of workers under load | `server reached pm.max_children`, 502s under traffic | Increase `pm.max_children` in the FPM pool config |
| Socket permission denied | `connect() failed (13: Permission denied)` | Align socket owner/group with the Nginx user |
| Resource exhaustion under traffic | intermittent 502s, `no live upstreams` | Add capacity, tune worker/connection limits |
Cause 1: The upstream backend is down
This is the most common 502 by a wide margin. The log says `Connection refused`. The backend process isn’t running, so when Nginx knocks, nobody answers.
“`bash systemctl status php8.2-fpm
systemctl restart php8.2-fpm “`
For a Node or Python app behind Nginx, check your process manager (systemd, pm2, supervisor) the same way. If the process keeps dying right after you restart it, that’s a clue the real problem is downstream — a crash on boot, a bad config, or memory. Check the application’s own log, not just Nginx’s.
Cause 2: PHP-FPM socket or pool problems
PHP-FPM is the upstream for most PHP sites, and it produces a disproportionate share of 502s. Three things go wrong:
Wrong socket path. Your Nginx config points `fastcgi_pass` at a socket that doesn’t exist (a common breakage after a PHP version upgrade — the path changes from `php8.1-fpm.sock` to `php8.2-fpm.sock`). The fix is to match them exactly:
“`nginx location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; # must match the FPM pool’s “listen” fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } “`
Confirm what FPM is actually listening on:
“`bash grep -R “listen =” /etc/php/8.2/fpm/pool.d/
“`
The two must be identical. A typo here produces an instant 502.
FPM ran out of workers. Under traffic you’ll see `server reached pm.max_children` in the FPM log and 502s in waves. Raise the ceiling in the pool config:
“`ini ; /etc/php/8.2/fpm/pool.d/www.conf pm = dynamic pm.max_children = 50 ; was too low for your traffic pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 15 “`
Then `systemctl reload php8.2-fpm`. Set `pm.max_children` based on available RAM divided by per-worker memory — don’t just crank it to a huge number or you trade a 502 for an OOM kill.
Socket permissions. If the log says `(13: Permission denied)`, the Nginx user can’t access the FPM socket. Make the socket owner/group match the web server user in the FPM pool config (`listen.owner`, `listen.group`).
Cause 3: The backend is too slow (timeout)
If your upstream is alive but takes longer to respond than Nginx is willing to wait, Nginx gives up. This often shows as a 504, but a backend that closes the connection while struggling can produce a 502 too. Raising the timeout buys time:
“`nginx
fastcgi_read_timeout 120s;
proxy_read_timeout 120s; proxy_connect_timeout 60s; “`
But be honest with yourself: a higher timeout is a bandage. If a request needs two minutes, the real fix is the slow query, the unindexed table, or the blocking external API call behind it. Raise the timeout to stop the bleeding, then go fix the backend.
Cause 4: Out of memory
When the server runs low on RAM, the Linux OOM-killer terminates a process — often your PHP-FPM worker or app server — mid-request. Nginx sees `upstream prematurely closed connection`. Confirm it:
“`bash dmesg -T | grep -i -E “killed process|out of memory”
“`
If the OOM-killer is firing, no amount of Nginx tuning helps. Add memory, lower `pm.max_children` so each worker fits, or reduce per-request memory use in the application.
Cause 5 & 6: Wrong upstream address, or too many connections
A wrong `proxy_pass` host or port — pointing at `127.0.0.1:3000` when the app listens on `3001` — gives a permanent 502 until corrected. And under heavy traffic, even a healthy backend can be overwhelmed: connection limits hit, workers exhausted, `no live upstreams` in the log. That’s a capacity problem. Tune `worker_connections`, scale the backend, and make sure your FPM/app worker counts match real demand.
How DarazHost keeps 502s rare
A lot of 502s come down to infrastructure that was never tuned for the workload — an FPM pool too small for the traffic, a socket path that drifted after an upgrade, a server with too little RAM for its worker count. DarazHost runs hosting where the web server and the PHP-FPM/backend stack are correctly configured and actively monitored, so the misconfigurations that cause most 502s are caught before they reach your visitors.
When you do need to get your hands dirty, our VPS plans give you root access to read `/var/log/nginx/error.log` yourself and tune Nginx and FPM directly — sockets, `*_read_timeout` values, `pm.max_children`, worker limits — without filing a ticket and waiting. You get reliable infrastructure, and a 24/7 support team that knows how to read an upstream failure and diagnose a 502 fast when it’s not obvious. The goal is simple: fewer 502s, and a clear path to the fix when one does appear.
How do you confirm the fix worked?
After you change a config, validate the syntax before reloading — a broken config is its own outage:
“`bash nginx -t
systemctl reload nginx “`
Then `tail -f /var/log/nginx/error.log` and hit the page again. A clean log and a 200 response means the backend is answering. If the same upstream error returns, the backend still isn’t fixed — go back to the log line and re-read which failure it names. The log is the source of truth, start to finish.
Frequently asked questions
Is a 502 Bad Gateway caused by Nginx or by my application? Almost always the application (the upstream backend). Nginx is reporting that the backend it proxies to — PHP-FPM, your Node or Python app — returned an invalid or empty response. Read `/var/log/nginx/error.log` to see which upstream failed and why. Restarting Nginx alone rarely helps.
Why does restarting PHP-FPM fix my 502? Because the backend was the problem. If PHP-FPM had crashed, run out of workers, or was OOM-killed, Nginx had nothing valid to proxy. Restarting FPM brings the upstream back. If it returns within minutes, look deeper — a crash loop, memory pressure, or `pm.max_children` set too low.
What does “upstream prematurely closed connection” mean? The backend accepted the request but died or closed the connection before finishing its response — frequently a crash or an out-of-memory kill mid-request. Check `dmesg` for the OOM-killer and check your application’s own log for a fatal error.
Will raising the Nginx timeout fix a 502? Only if the cause is a slow backend that’s still alive. Raising `fastcgi_read_timeout` or `proxy_read_timeout` stops Nginx giving up too early. It will not help if the backend is down, misconfigured, or out of memory — and it’s a bandage over a slow request that should really be fixed at the source.
Can a wrong socket path cause a 502? Yes — one of the most common causes. If `fastcgi_pass` points at a socket that doesn’t exist (often after a PHP version upgrade changes the path), Nginx gets `connect() failed (Connection refused)` and returns a 502. Match the `fastcgi_pass` value to the FPM pool’s `listen =` value exactly.