How to Fix Nginx 502 Bad Gateway in 5 Minutes
A 502 Bad Gateway error means Nginx successfully received a request from the client but got an invalid response from the upstream server it was proxying to. In plain terms: Nginx is working fine, but the backend service it forwards requests to is either down, misconfigured, or crashing.
This is one of the most common production errors. It is rarely a Nginx bug itself. The upstream — PHP-FPM, Node.js, Python Gunicorn, or any other app server — is the source. Follow these five steps to identify and fix the root cause in under five minutes.
Step 1: Check if the Upstream Process Is Running
The most common cause of a 502 is that the upstream service simply stopped. Before anything else, confirm it is running:
# For PHP-FPM
systemctl status php-fpm
# For Node.js (via PM2)
pm2 status
# For Python / Gunicorn
systemctl status gunicorn
# For Ruby / Puma (systemd)
systemctl status puma
Look for the Active: active (running) line. If the service shows inactive or failed, restart it and check if the 502 clears:
# Restart the upstream service
systemctl restart php-fpm
# Or with PM2
pm2 restart all
If the service restarts but immediately crashes again, check the service-specific log (Step 3 below) for the real error.
Step 2: Verify the Upstream Configuration in nginx.conf
If the upstream is running, the next most likely cause is a mismatch between what Nginx expects and where the upstream is actually listening. Open your Nginx configuration and find the proxy_pass or upstream block:
# Inside your server block or upstream block
upstream php_backend {
server unix:/run/php/php-fpm.sock;
# or
server 127.0.0.1:9000;
}
server {
location ~ \.php$ {
fastcgi_pass php_backend;
# ...
}
}
Common mistakes to look for:
- Wrong socket path: The
.sockfile path does not match what the upstream is configured to listen on. Checklisten = /run/php/php-fpm.sockin your PHP-FPM pool config. - Wrong port: The upstream is listening on port
9001but Nginx sends to9000. - IPv4 vs IPv6: Nginx sends to
127.0.0.1:9000but upstream binds to[::1]:9000, or vice versa. - Multiple upstream blocks with conflicting names.
After any change, test and reload Nginx:
nginx -t # Test configuration syntax
nginx -s reload # Reload without dropping connections
Step 3: Check Nginx and Upstream Logs
The error logs will almost always tell you exactly what went wrong. Check both Nginx and the upstream service:
# Nginx error log (most useful for 502s)
tail -50 /var/log/nginx/error.log
# PHP-FPM error log
tail -50 /var/log/php-fpm/error.log
# or, depending on your pool config:
tail -50 /var/log/php8.2-fpm/error.log
# Node.js app log
pm2 logs --lines 50
# Gunicorn log
journalctl -u gunicorn --no-pager -n 50
Key error messages to watch for:
connect() failed (111: Connection refused)— upstream is not running or is on a different port.connect() to unix:/run/php/php-fpm.sock failed (13: Permission denied)— Nginx cannot read the socket file (see Step 4).upstream prematurely closed connection— the upstream crashed while processing the request.recv() failed (104: Connection reset by peer)— upstream forcibly closed the connection, often due to a timeout or OOM kill.
Step 4: Apply Common Fixes
Based on the log output, apply the appropriate fix from the table below:
4a. Restart the Upstream Service
systemctl restart php-fpm
If the service keeps crashing, investigate the upstream error log. Common culprits: out-of-memory (OOM killer), corrupted PHP files, or a broken dependency update.
4b. Fix Socket Permissions
If the error log shows Permission denied on the socket file, Nginx (usually running as nginx or www-data) cannot access it:
# Check the socket file owner and permissions
ls -la /run/php/php-fpm.sock
# The PHP-FPM pool config controls this:
# /etc/php/8.2/fpm/pool.d/www.conf
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
After updating the pool config, restart PHP-FPM:
systemctl restart php-fpm
4c. Increase Proxy Buffer Sizes
Sometimes the upstream sends headers or body content that exceed Nginx's default buffer limits, causing a 502. Add these directives inside your location, server, or http block:
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_read_timeout 300;
Then reload Nginx:
nginx -s reload
4d. Increase Upstream Timeout Settings
If slow upstream responses cause the 502, adjust the timeout values:
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
Step 5: Verify the Fix
After applying any fix, verify that the 502 is resolved:
# Quick local test
curl -I http://localhost
# Expected response:
# HTTP/1.1 200 OK
# Content-Type: text/html; charset=UTF-8
# ...
# Test from external (replace with your domain)
curl -I https://example.com
You should see a 200 OK (or 301/302 redirect), not a 502 Bad Gateway. Also load the site in a browser to confirm end-to-end functionality.
Quick Reference: Causes and Fixes
| Symptom / Log Message | Root Cause | Fix |
|---|---|---|
Connection refused |
Upstream not running or wrong port | Start upstream; verify port/socket in config |
Permission denied on socket |
Nginx user cannot read socket file | Set listen.owner and listen.mode in pool config |
upstream prematurely closed connection |
Upstream crashed (OOM, fatal error) | Check upstream log; increase memory limit |
| 502 only on large pages | Proxy buffer too small | Increase proxy_buffer_size and proxy_buffers |
| 502 on slow requests | Timeout exceeded | Increase fastcgi_read_timeout or proxy_read_timeout |
| 502 after server reboot | Upstream service not enabled at boot | systemctl enable php-fpm |
Pro tip: If you are running behind Cloudflare or another CDN and see a 502 in the browser but not in curl from the server, the 502 may originate from the CDN layer. Check your CDN dashboard for origin connection errors.