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