Error Medic

nginx 502 Bad Gateway: Complete Troubleshooting Guide for All Upstream Failures

Fix nginx 502 Bad Gateway, 504, connection refused, crash, and OOM errors with step-by-step diagnosis commands. Restore your upstream in minutes.

Last updated:
Last verified:
2,258 words
Key Takeaways
  • nginx 502 Bad Gateway means nginx received an invalid or no response from the upstream server (Node.js, PHP-FPM, Gunicorn, etc.) — the backend is down, misconfigured, or crashing mid-request
  • nginx 504 Gateway Timeout differs from 502: the upstream is reachable but too slow; 'connection refused' in the error log means the upstream port or Unix socket is not listening at all
  • nginx crashes (core dumps, OOM kills), 'too many connections', high CPU, and permission denied errors each require distinct OS-level fixes: worker limits, open-file descriptors, socket ownership, and upstream keepalive tuning
  • Quick fix checklist: (1) confirm upstream process is running and on the correct port, (2) verify proxy_pass address/port, (3) read upstream application logs, (4) reload nginx after any config change with nginx -t && systemctl reload nginx, (5) increase timeouts or connection limits if the issue is load-related
Fix Approaches Compared
MethodWhen to UseTimeRisk
Restart the upstream process502 or connection refused; backend crashed or was never started< 1 minLow — brief downtime during restart
Correct proxy_pass address or port502 after deployment or config change; wrong upstream target< 2 minLow — requires nginx reload
Increase proxy_read_timeout / proxy_connect_timeout504 or intermittent 502 under load; upstream is alive but slow< 2 minLow — may mask slow backends long-term
Raise worker_rlimit_nofile and worker_connectionsnginx 'too many open files' or 'worker_connections are not enough'; high-traffic sites5 minLow — requires nginx reload
Add upstream keepalive poolHigh CPU, connection churn, slow response under sustained load10–30 minMedium — validate with load test
Fix Unix socket file permissionspermission denied in error log; nginx cannot connect to PHP-FPM or uWSGI socket5 minLow — verify user/group match after change
Investigate OOM kill or segfault / core dumpnginx crashes randomly; OOM messages in dmesg; core files present30–60 minMedium — may require kernel tuning or nginx upgrade

Understanding nginx 502 Bad Gateway and Related Upstream Errors

When nginx returns 502 Bad Gateway, the server successfully received your HTTP request but could not obtain a valid response from the upstream server it was proxying to. This is one of several related failure modes:

  • 502 Bad Gateway — upstream returned an invalid response or closed the connection unexpectedly
  • 504 Gateway Timeout — upstream is alive but exceeded proxy_read_timeout before responding
  • 502 with 'connection refused' — nothing is listening on the upstream address/port
  • nginx not starting at all — the nginx process itself failed at boot

Identifying which variant you face is the essential first diagnostic step.

Step 1: Read the Exact Error Message in the Log

Always begin with nginx's error log:

tail -n 100 /var/log/nginx/error.log
# For a specific virtual host:
tail -n 100 /var/log/nginx/mysite.error.log

Key phrases and their root causes:

Log excerpt Root cause
connect() failed (111: Connection refused) Upstream process not running or wrong port
recv() failed (104: Connection reset by peer) Upstream crashed mid-request
upstream timed out (110: Connection timed out) Upstream too slow — results in 504, not 502
no live upstreams while connecting to upstream All servers in the upstream pool are down
(13: Permission denied) nginx user cannot access the Unix socket
worker_connections are not enough Raise worker_connections and worker_rlimit_nofile

Step 2: Confirm the Upstream Process Is Running

For PHP-FPM:

systemctl status php8.2-fpm
ss -tlnp | grep 9000
# Or for a Unix socket:
ls -la /run/php/php8.2-fpm.sock

For Node.js, Gunicorn, or uWSGI:

systemctl status myapp
ps aux | grep -E 'node|gunicorn|uwsgi'
ss -tlnp | grep 3000   # adjust port to match your app

If the process is dead, restart it and watch the logs:

systemctl restart myapp
journalctl -u myapp -f

Step 3: Verify the proxy_pass Configuration

A mismatched port or address in the nginx config is a frequent cause of 502 after deployments. Example correct configuration:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;   # must match the app's listening port
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Always test before reloading:

nginx -t && systemctl reload nginx

Step 4: Fix Timeout-Related 502 and 504 Errors

If the upstream is alive but slow (heavy database queries, external API calls), increase the timeout directives:

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

For WebSocket applications or long-polling endpoints, values up to 300s or higher may be appropriate.

Step 5: Diagnose nginx Service Not Starting

If systemctl start nginx fails immediately, run:

nginx -t                          # find config syntax errors — output shows file and line
journalctl -u nginx -n 50         # full systemd journal for the unit
ss -tlnp | grep ':80\|:443'      # another process already on the port?

Common causes of nginx failing to start:

  • Port conflict: Apache or a previous nginx process holds port 80/443. Identify the owner with ss -tlnp | grep ':80' and stop it.
  • Config syntax error: nginx -t will print the exact file and line number.
  • Missing or expired SSL certificate: The path in ssl_certificate does not exist or Let's Encrypt certificate has expired.

Step 6: Fix Permission Denied on Unix Sockets

When nginx proxies to a PHP-FPM or uWSGI Unix socket, the nginx worker user must have read/write access to the socket file:

ls -la /run/php/php8.2-fpm.sock
# Expected: srw-rw---- 1 www-data www-data ...

Ensure nginx runs as the same user or group that owns the socket:

# /etc/nginx/nginx.conf
user www-data;

Configure PHP-FPM to set the socket permissions explicitly:

; /etc/php/8.2/fpm/pool.d/www.conf
listen.owner = www-data
listen.group = www-data
listen.mode  = 0660

Restart both PHP-FPM and nginx after changing ownership settings.

Step 7: Resolve nginx High CPU and Too Many Connections

nginx high CPU is typically caused by:

  1. Too few worker processes — set worker_processes auto; to use all CPU cores automatically
  2. Connection churn — the upstream connection is torn down and rebuilt for every request. Add keepalive to the upstream block:
upstream backend {
    server 127.0.0.1:3000;
    keepalive 64;   # number of idle connections to keep open per worker
}

For 'worker_connections are not enough' warnings, increase limits in /etc/nginx/nginx.conf:

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

Also raise the OS-level file descriptor limit:

echo 'nginx  soft  nofile  65535' >> /etc/security/limits.conf
echo 'nginx  hard  nofile  65535' >> /etc/security/limits.conf
sysctl -w fs.file-max=200000
echo 'fs.file-max = 200000' >> /etc/sysctl.conf

Step 8: Investigate nginx Crash, Core Dump, and Out-of-Memory Kills

If nginx workers die unexpectedly:

# Check OOM killer activity
dmesg | grep -i 'oom\|killed process'
journalctl -k | grep nginx

# Check for core dump files
find /var/crash /tmp /var/log -name 'core*' 2>/dev/null

# Check crash signals in journal
journalctl -u nginx --since '2 hours ago' | grep -iE 'signal|crash|segfault|coredump'

For OOM kills: reduce the number of worker processes, add system swap, or increase RAM. For segfaults (core dumps), check the nginx version first — upgrade to the latest stable release. If crashes persist, disable third-party modules one by one. Analyze a core file with:

gdb /usr/sbin/nginx /path/to/core
(gdb) bt full

Step 9: Fix nginx Slow Response

Slowness unrelated to upstream processing can have several nginx-side causes:

  1. DNS resolution latency — if proxy_pass uses a hostname, nginx resolves it at config load only. Use IP addresses directly or add a resolver directive.
  2. Buffering misconfiguration — for streaming or large file transfers:
proxy_buffering off;
# or for large API responses:
proxy_buffer_size 128k;
proxy_buffers     4 256k;
  1. Missing compression — enable gzip for text payloads:
gzip on;
gzip_min_length 1024;
gzip_types text/plain application/json application/javascript text/css;
  1. Missing sendfile / TCP tuning for static asset serving:
http {
    sendfile       on;
    tcp_nopush     on;
    tcp_nodelay    on;
    keepalive_timeout 65;
}

Frequently Asked Questions

bash
#!/usr/bin/env bash
# nginx-diagnose.sh — rapid triage for 502/504/crash/slow/permission issues
# Run as root or with sudo

set -euo pipefail

echo '============================================================'
echo ' nginx diagnostic report'
echo ' date:' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo '============================================================'

echo ''
echo '--- nginx version ---'
nginx -v 2>&1

echo ''
echo '--- nginx service status ---'
if systemctl is-active --quiet nginx; then
  echo 'nginx: ACTIVE'
else
  echo 'nginx: NOT ACTIVE'
  systemctl status nginx --no-pager -l | tail -20
fi

echo ''
echo '--- nginx config test ---'
nginx -t 2>&1

echo ''
echo '--- last 30 error log lines ---'
ERROR_LOG=$(nginx -T 2>/dev/null | grep 'error_log' | awk '{print $2}' | head -1)
ERROR_LOG=${ERROR_LOG:-/var/log/nginx/error.log}
if [ -f "$ERROR_LOG" ]; then
  tail -30 "$ERROR_LOG"
else
  echo "Error log not found at $ERROR_LOG"
fi

echo ''
echo '--- listening ports (nginx) ---'
ss -tlnp | grep nginx || echo 'No nginx sockets found'

echo ''
echo '--- common upstream processes ---'
for svc in php-fpm php8.2-fpm php8.1-fpm node gunicorn uvicorn uwsgi puma unicorn; do
  if pgrep -x "$svc" > /dev/null 2>&1; then
    echo "RUNNING : $svc (PID $(pgrep -x $svc | head -1))"
  else
    echo "ABSENT  : $svc"
  fi
done

echo ''
echo '--- upstream TCP ports in use ---'
ss -tlnp | grep -E ':3000|:5000|:8000|:8080|:9000' || echo 'None of the common upstream ports detected'

echo ''
echo '--- Unix sockets (PHP-FPM / uWSGI) ---'
find /run /tmp /var/run -name '*.sock' 2>/dev/null | while read -r sock; do
  echo "$(ls -la $sock)"
done

echo ''
echo '--- OOM killer events (last 10) ---'
dmesg | grep -iE 'oom|killed process|out of memory' | tail -10 || echo 'No OOM events found'

echo ''
echo '--- nginx core dumps ---'
find /var/crash /tmp /var/log /core* /proc/*/cwd -name 'core*' 2>/dev/null | head -5 || echo 'No core dumps found'

echo ''
echo '--- open file descriptors (nginx master) ---'
NGINX_PID=$(pgrep -o nginx 2>/dev/null || true)
if [ -n "$NGINX_PID" ]; then
  FD_COUNT=$(ls /proc/$NGINX_PID/fd 2>/dev/null | wc -l)
  echo "nginx master PID: $NGINX_PID | open fds: $FD_COUNT"
else
  echo 'nginx master process not found'
fi

echo ''
echo '--- connection summary ---'
ss -s | grep -E 'TCP|estab|closed|time-wait'

echo ''
echo '--- worker_connections configured ---'
grep -rn 'worker_connections\|worker_rlimit_nofile\|worker_processes' /etc/nginx/ 2>/dev/null || echo 'No nginx config found at /etc/nginx/'

echo ''
echo '--- OS file descriptor limit ---'
echo "fs.file-max = $(cat /proc/sys/fs/file-max)"

echo ''
echo '============================================================'
echo ' Diagnosis complete. Check errors above for root cause.'
echo '============================================================'
E

Error Medic Editorial

Error Medic Editorial is a team of senior DevOps engineers, SREs, and infrastructure architects with combined experience across Linux production systems, Kubernetes clusters, cloud platforms, and high-traffic web stacks. Every guide is grounded in real incident post-mortems and cross-referenced against official upstream documentation.

Sources

Related Articles in Nginx

Explore More Linux Sysadmin Guides