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:

MessageMeaning
connect() failed (111: Connection refused) while connecting to upstreamNothing is listening at that address and port
upstream timed out (110: Connection timed out)The app took too long
no live upstreamsEvery 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. localhost can 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:

  1. Is the server overloaded? Use htop and free -h. See How to monitor your VPS with a few basic command line tools.
  2. Is the disk full? See Fixing "No space left on device".
  3. Is a database query or external call hanging? Look at the application's log.
  4. 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 killed and 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

SymptomStart here
502 on every pageApp stopped or wrong address
502 after a PHP updateSocket path or version
504 on one slow pageThat page's query or timeout
504 under loadCPU, memory or database limits
Works by curl, fails in nginxConfig, 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.