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.groupin pool config - Upstream timed out โ raise
proxy_read_timeout, investigate slow app code - Wrong port โ update
proxy_passto 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.

