Fix Nginx 502 Bad Gateway Error

intermediateโšก Nginx2026-03-17| Ubuntu 20.04/22.04, Nginx 1.18+, upstream: Node.js / PHP-FPM / Gunicorn / any reverse-proxy backend

Error Message

502 Bad Gateway nginx
#nginx#502#proxy#upstream

What's actually happening

502 means Nginx got a bad or empty response from the backend it's proxying to โ€” Node.js, PHP-FPM, Gunicorn, or any app server you've configured. Nginx itself is fine. The problem is upstream.

In practice, it almost always traces back to one of these four things:

  • The upstream service has crashed or was never started
  • The upstream address or port in your Nginx config is wrong
  • The upstream is overloaded and timing out
  • A Unix socket path doesn't exist or has wrong permissions

Step 1 โ€” Check the Nginx error log

Don't guess. Start here:

sudo tail -n 50 /var/log/nginx/error.log

Look for one of these lines โ€” each points to a different fix:

# Upstream refused connection
connect() failed (111: Connection refused) while connecting to upstream

# Upstream timed out
upstream timed out (110: Connection timed out) while reading response header

# Unix socket missing
connect() to unix:/run/php/php8.1-fpm.sock failed (2: No such file or directory)

Step 2 โ€” Check if the upstream service is running

For PHP-FPM:

sudo systemctl status php8.1-fpm

For Node.js (PM2):

pm2 list
pm2 logs

For Gunicorn / Django:

sudo systemctl status gunicorn

A stopped or failed service needs a restart:

# PHP-FPM
sudo systemctl restart php8.1-fpm

# PM2 Node.js
pm2 restart all

# Gunicorn
sudo systemctl restart gunicorn

Reload the page. 502 gone? You're done. Still there? Keep reading.

Step 3 โ€” Verify the proxy address in your Nginx config

Pull up your site config:

sudo nano /etc/nginx/sites-available/your-site

Find the proxy_pass or fastcgi_pass line:

# Node.js โ€” port must match what your app is actually listening on
location / {
    proxy_pass http://127.0.0.1:3000;
}

# PHP-FPM โ€” socket path must exist on disk
location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}

Confirm what port your app is actually using:

sudo ss -tlnp | grep LISTEN

Sample output:

LISTEN  0  128  127.0.0.1:3000  ...  users:(("node",pid=12345))

Port mismatch? Update proxy_pass and reload Nginx:

sudo nginx -t && sudo systemctl reload nginx

Step 4 โ€” Fix socket permissions (PHP-FPM)

Permission denied on the socket means ownership is wrong. Check it:

ls -la /run/php/php8.1-fpm.sock

The socket should be owned by www-data (or whichever user Nginx runs as). Open the PHP-FPM pool config:

sudo nano /etc/php/8.1/fpm/pool.d/www.conf

These three lines need to match your Nginx user:

listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Restart PHP-FPM to apply:

sudo systemctl restart php8.1-fpm

Step 5 โ€” Handle upstream timeouts

Log says upstream timed out? Your app is taking too long to respond. Add timeout directives to your Nginx server block:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_connect_timeout 60s;
    proxy_send_timeout    60s;
    proxy_read_timeout    60s;
}

Reload after saving:

sudo nginx -t && sudo systemctl reload nginx

This buys time โ€” but it's not a real fix. If your app is consistently slow, dig into the root cause: slow database queries, blocking I/O, or resource exhaustion are the usual suspects.

Verify the fix

  • Reload the page in your browser โ€” the 502 should be gone.
  • Confirm with curl:
curl -I https://yourdomain.com

HTTP/2 200 means everything is working. A 301 or 302 is fine too โ€” those are just redirects. Anything that isn't a 5xx means Nginx is successfully passing traffic to the backend.

Let the error log run for a minute to make sure nothing new pops up:

sudo tail -f /var/log/nginx/error.log

Quick reference โ€” 502 causes and fixes

  • Connection refused โ†’ upstream service is down, restart it
  • No such file or directory (socket) โ†’ wrong socket path in config, or PHP-FPM not running
  • Permission denied (socket) โ†’ fix listen.owner / listen.group in pool config
  • Upstream timed out โ†’ raise proxy_read_timeout, investigate slow app code
  • Wrong port โ†’ update proxy_pass to match actual app port

One last thing

Most 502s come down to one thing: Nginx can't reach the backend. The log file tells you exactly why โ€” don't skip it.

Going forward, run your upstream services under systemd or PM2 so they restart automatically on crash. Pair that with uptime monitoring โ€” UptimeRobot and Better Stack both have free tiers โ€” and you'll catch the next outage before your users do.

Related Error Notes