How to fix nginx 502 Bad Gateway and 504 Gateway Timeout errors
You set up nginx in front of an application, and now the browser shows 502 Bad Gateway or 504 Gateway Timeout. These errors have a specific meaning: nginx itself is working, but it could not get a proper answer from the program behind it. That narrows the search a lot. The fault is almost always the application, or the connection between nginx and the application.
What the two errors mean
- 502 Bad Gateway: nginx tried to talk to the upstream program and got nothing usable. Usually the program is not running, is listening somewhere else, or crashed.
- 504 Gateway Timeout: nginx connected but waited too long for a reply. The program is running but slow or stuck.
A third, 503, usually means the application or a limit is deliberately refusing requests.
Step 1: read the nginx error log
The browser says little, but nginx writes the reason down:
sudo tail -n 50 /var/log/nginx/error.log
Look for lines like these:
| Message | Meaning |
|---|---|
connect() failed (111: Connection refused) while connecting to upstream | Nothing is listening at that address and port |
upstream timed out (110: Connection timed out) | The app took too long |
no live upstreams | Every configured backend is down |
connect() to unix:/run/app.sock failed (2: No such file or directory) | The socket file does not exist |
(13: Permission denied) | nginx cannot access the socket |
How to read your server logs explains how to find the first real error in a long log.
Step 2: is the application running?
Most 502 errors are this simple. Check the service:
sudo systemctl status myapp
pm2 status
If it has stopped, read its log to see why, restart it and make sure it starts at boot. See How to run your own program as a systemd service and Run a Node.js app on your VPS with PM2 and nginx.
Step 3: does the address match?
Compare what the app listens on with what nginx expects. Find the listening ports:
sudo ss -tulpn | grep LISTEN
If your app listens on port 3000 but nginx has proxy_pass http://127.0.0.1:8080;, you will get a 502. Fix whichever is wrong. Also watch for:
- localhost versus 127.0.0.1.
localhostcan resolve to IPv6 (::1), while the app only listens on IPv4. - An app bound to a different interface, such as the server's public IP rather than
127.0.0.1. - A Docker container whose port is not published. See Docker Compose basics for small projects.
Step 4: sockets and permissions
If you use a Unix socket, for example with PHP-FPM, check that the path is the same in both configurations and that nginx's user (often www-data) can read and write it:
ls -l /run/php/
Mismatched PHP versions are common: a site configured for php8.1-fpm.sock while only php8.3-fpm is installed gives a 502 on every page.
Step 5: for 504, find out why it is slow
A 504 means the app is working but not answering in time. Check, in order:
- Is the server overloaded? Use
htopandfree -h. See How to monitor your VPS with a few basic command line tools. - Is the disk full? See Fixing "No space left on device".
- Is a database query or external call hanging? Look at the application's log.
- Is it a legitimately long request, such as a large upload or report?
Only raise the timeouts once you know the work is legitimate:
proxy_connect_timeout 60s;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
For PHP use fastcgi_read_timeout. Raising timeouts on a genuinely stuck app only makes users wait longer to fail.
Step 6: test the configuration
After any change:
sudo nginx -t
sudo systemctl reload nginx
nginx -t checks for syntax errors before you apply anything, which prevents taking the site down with a typo.
Step 7: test the app directly
Bypass nginx and talk to the program yourself:
curl -i http://127.0.0.1:3000/
If this fails, the problem is in the application, and nginx is blameless. If it works, look at the nginx config, headers and upstream definition.
Other causes worth checking
- SELinux or AppArmor blocking connections on some systems.
- Firewall rules blocking local traffic, though rarely on the loopback interface.
- Memory exhaustion, where the kernel ended your app. Check
dmesg | grep -i killedand consider adding swap space. - Huge request headers or cookies exceeding buffer sizes.
- A recent deploy that changed the port or start command.
Quick decision guide
| Symptom | Start here |
|---|---|
| 502 on every page | App stopped or wrong address |
| 502 after a PHP update | Socket path or version |
| 504 on one slow page | That page's query or timeout |
| 504 under load | CPU, memory or database limits |
| Works by curl, fails in nginx | Config, headers or permissions |
Still stuck? Open a ticket on Discord - run /ticket in any channel, pick a department, and include your Server ID or Invoice ID so we can jump straight in.