← Back to DevBytes

Fix Nginx '502 Bad Gateway' Error: Complete Troubleshooting Guide

Understanding the Nginx 502 Bad Gateway Error

A 502 Bad Gateway error occurs when Nginx, acting as a reverse proxy or gateway, receives an invalid or incomplete response from an upstream server. In simple terms, Nginx forwards a client request to a backend service (like PHP-FPM, a Node.js application, Apache, or another proxied server), but that backend fails to return a proper HTTP response. Nginx then returns the 502 status code to the client, signaling that something went wrong behind the scenes.

The error typically looks like this in a browser:

502 Bad Gateway
nginx/1.24.0

Or in Nginx's error log:

[error] 12345#0: *12345 upstream prematurely closed connection while reading response header from upstream

Why This Error Matters

A 502 error directly impacts user experience and service availability. For production websites, every minute of downtime can translate into lost revenue, damaged reputation, and frustrated users. For developers, understanding how to quickly diagnose and resolve 502 errors is an essential operational skill. The error is particularly critical because it doesn't always point to a single root cause — it can stem from application crashes, misconfigured services, network issues, resource exhaustion, or permission problems. A systematic troubleshooting approach is therefore invaluable.

Common Root Causes of 502 Bad Gateway

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Before diving into fixes, it's helpful to understand the most frequent triggers. Here are the primary categories:

Step-by-Step Troubleshooting Workflow

Follow this systematic sequence to isolate and fix the problem efficiently. Always start with the simplest checks and move toward more complex diagnostics.

Step 1: Check If the Backend Service Is Running

Use ps and systemctl to verify that the upstream service is alive. For PHP-FPM:

# Check process list for PHP-FPM
ps aux | grep php-fpm

# Check systemd service status
systemctl status php-fpm
# or on older systems:
systemctl status php7.4-fpm
systemctl status php8.2-fpm

For a Node.js application managed by PM2:

pm2 list
pm2 status app-name

If the service is not running, start it immediately:

sudo systemctl start php8.2-fpm
sudo systemctl enable php8.2-fpm  # ensure it starts on boot

# For PM2:
pm2 start app.js --name my-app
pm2 save

Step 2: Verify the Backend's Listening Address

Confirm that the backend is actually listening on the address Nginx expects. For TCP-based services, use netstat or ss:

# Check listening ports
sudo ss -tlnp | grep -E '9000|3000|8000'
sudo netstat -tlnp | grep -E '9000|3000|8000'

For Unix sockets, check that the socket file exists:

ls -la /var/run/php/php8.2-fpm.sock
ls -la /run/php/php8.2-fpm.sock

Compare the output with your Nginx configuration. A mismatch here is a very common culprit.

Step 3: Examine Nginx Configuration

Open your Nginx site configuration file (typically in /etc/nginx/sites-available/ or /etc/nginx/conf.d/) and inspect the proxy or FastCGI directives. Look for the correct port or socket path:

# Example for PHP-FPM with TCP socket
location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

# Example for PHP-FPM with Unix socket
location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

# Example for Node.js proxy
location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

After any configuration change, validate and reload Nginx:

sudo nginx -t           # test configuration syntax
sudo systemctl reload nginx
# or
sudo nginx -s reload

Step 4: Check Nginx and Backend Logs

Logs are your most powerful diagnostic tool. Check both Nginx's error log and the backend service's logs simultaneously:

# Tail Nginx error log
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/nginx/access.log

# Tail PHP-FPM log (path varies by distribution)
sudo tail -f /var/log/php-fpm.log
sudo tail -f /var/log/php8.2-fpm.log
sudo tail -f /var/log/fpm-php.www.log

# For systemd-based PHP-FPM, use journalctl
sudo journalctl -u php8.2-fpm -f

Common log entries and what they indicate:

# Nginx error log examples:

# 1. Backend connection refused
connect() failed (111: Connection refused) while connecting to upstream

# 2. Backend timeout
upstream timed out (110: Connection timed out) while reading response header from upstream

# 3. Premature connection close
upstream prematurely closed connection while reading response header from upstream

# 4. Permission denied on Unix socket
connect() to unix:/var/run/php/php-fpm.sock failed (13: Permission denied)

# 5. Upstream too many pending connections (overload)
*12345 upstream queue is full, consider increasing proxy_max_temp_file_size

PHP-FPM log examples:

# PHP fatal error causing child termination
[pool www] server reached pm.max_children setting (5), consider raising it

# Memory exhaustion
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted

# Segmentation fault in PHP extension
child 12345 exited on signal 11 (SIGSEGV) after 120.000000 seconds

Step 5: Fix PHP-FPM Specific Issues

PHP-FPM is one of the most common upstream servers behind Nginx. Here are targeted fixes for frequent PHP-FPM problems.

5a. Unix Socket Permission Denied

If you see "Permission denied" when Nginx tries to connect to the PHP-FPM socket, check the socket file permissions and the user Nginx runs as:

# Check socket ownership and permissions
ls -l /var/run/php/php8.2-fpm.sock

# Check Nginx worker user
grep '^user' /etc/nginx/nginx.conf

The typical fix is to align the socket permissions or switch to a TCP connection. For socket-based setups, ensure the socket is readable/writable by the Nginx user (often www-data or nginx). Edit the PHP-FPM pool configuration:

# File: /etc/php/8.2/fpm/pool.d/www.conf (adjust version as needed)

; Set listen owner and group to match Nginx user
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; Or switch to TCP if socket permissions are persistently problematic
; listen = 127.0.0.1:9000

Restart PHP-FPM after changes:

sudo systemctl restart php8.2-fpm

5b. PHP-FPM Max Children Exhausted

When all PHP-FPM child processes are busy, new requests queue up or fail. This appears in the PHP-FPM log as "server reached pm.max_children setting." Adjust the pool settings:

# File: /etc/php/8.2/fpm/pool.d/www.conf

pm = dynamic                ; or ondemand or static
pm.max_children = 50        ; increase from default 5-10
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500       ; recycle processes after 500 requests to prevent memory leaks

Calculate appropriate values based on available RAM. Each PHP-FPM child typically consumes 30-80 MB. Monitor with:

# Check current PHP-FPM process count and memory usage
ps aux | grep php-fpm | wc -l
ps aux | grep php-fpm | awk '{sum+=$6} END {print sum/1024 " MB"}'

5c. PHP Script Fatal Errors

If a PHP script has a fatal error, the child process may crash. Check PHP error logs:

# Check PHP error log location
grep 'error_log' /etc/php/8.2/fpm/php.ini

# Common location
sudo tail -f /var/log/php_errors.log

Enable more verbose PHP error logging temporarily:

# In php.ini or pool configuration
php_admin_value[error_log] = /var/log/php_errors.log
php_admin_value[log_errors] = On
php_admin_value[display_errors] = Off

Step 6: Fix Proxy Timeout and Buffer Issues

When Nginx proxies to a backend (Node.js, Python, Go, another web server), timeouts and buffer sizes become critical. Adjust these in your Nginx configuration:

# Inside location block or server block
location / {
    proxy_pass http://backend_server;

    # Increase timeouts for long-running backend operations
    proxy_read_timeout 120s;       # default is 60s
    proxy_connect_timeout 30s;     # default is 60s
    proxy_send_timeout 120s;       # default is 60s

    # Increase buffer sizes for large response headers
    proxy_buffer_size 16k;         # default is 4k or 8k
    proxy_buffers 8 32k;           # default is 8 4k or 8 8k
    proxy_busy_buffers_size 64k;   # default is 8k or 16k

    # Handle WebSocket / long-lived connections
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # Disable buffering for certain applications (streaming, SSE)
    # proxy_buffering off;
}

If you're using FastCGI (PHP-FPM, Python WSGI via FastCGI), adjust FastCGI-specific timeouts and buffers:

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;

    fastcgi_read_timeout 120s;     # default is 60s
    fastcgi_connect_timeout 30s;
    fastcgi_send_timeout 120s;

    fastcgi_buffer_size 16k;
    fastcgi_buffers 8 32k;
    fastcgi_busy_buffers_size 64k;

    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

Step 7: Check Firewall and SELinux

On systems with strict firewall rules or SELinux enabled, Nginx may be blocked from making outbound connections to the backend:

# Check firewall status
sudo ufw status
sudo firewall-cmd --list-all

# Check SELinux status
getenforce

# Check SELinux audit logs for denials
sudo ausearch -m AVC -ts recent | grep nginx
sudo grep nginx /var/log/audit/audit.log | tail -20

To allow Nginx to make network connections when SELinux is enforcing:

# Allow Nginx to make outbound HTTP connections
sudo setsebool -P httpd_can_network_connect on

# For Unix socket connections, check context
ls -Z /var/run/php/php8.2-fpm.sock
sudo semanage fcontext -a -t httpd_var_run_t "/var/run/php/php8.2-fpm.sock"
sudo restorecon -v /var/run/php/php8.2-fpm.sock

Step 8: Diagnose Backend Application Crashes

If the backend is a custom application (Node.js, Python, Go, Ruby), it may be crashing on certain requests. To isolate:

# Run the backend manually to see crash output
# For Node.js:
node app.js
# For Python Gunicorn:
gunicorn --bind 127.0.0.1:8000 app:application --log-level debug

# Check process manager logs (PM2)
pm2 logs my-app --lines 100

# Check systemd journal for backend service
sudo journalctl -u my-node-app -f

Common application-level causes:

Fix application issues by adding proper error handling, setting request timeouts in the application code, and implementing health checks:

// Node.js Express example: global error handler
app.use((err, req, res, next) => {
    console.error('Unhandled error:', err.stack);
    res.status(500).json({ error: 'Internal server error' });
});

// Add request timeout middleware
app.use((req, res, next) => {
    res.setTimeout(30000, () => {
        res.status(504).json({ error: 'Request timeout' });
    });
    next();
});

Step 9: Test Backend Directly

Bypass Nginx entirely to confirm the backend works on its own. Use curl or a browser pointed directly at the backend's listening address:

# Test TCP backend directly
curl -v http://127.0.0.1:9000/status  # adjust port for your backend
curl -v http://127.0.0.1:3000/

# Test Unix socket directly
curl --unix-socket /var/run/php/php8.2-fpm.sock http://localhost/status

# For PHP-FPM, use cgi-fcgi (install with: apt install libfcgi-bin)
sudo cgi-fcgi -connect /var/run/php/php8.2-fpm.sock /status
SCRIPT_FILENAME=/var/www/html/index.php QUERY_STRING="" REQUEST_METHOD=GET cgi-fcgi -connect /var/run/php/php8.2-fpm.sock

If the backend responds correctly when accessed directly but fails through Nginx, the problem is definitively in the Nginx-to-backend connection layer.

Prevention and Best Practices

1. Implement Robust Health Checks

Configure Nginx to actively check backend health before forwarding requests. Use the nginx upstream health check module or implement passive checks with proxy_next_upstream:

upstream backend_cluster {
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
}

server {
    location / {
        proxy_pass http://backend_cluster;
        proxy_next_upstream error timeout invalid_header http_502 http_503;
        proxy_next_upstream_tries 2;
    }
}

2. Set Up Proper Monitoring and Alerting

Monitor Nginx error logs and backend health proactively:

# Simple log-based monitoring with grep and cron
# Script: /usr/local/bin/check_502.sh
#!/bin/bash
COUNT=$(grep "502" /var/log/nginx/access.log | wc -l)
if [ "$COUNT" -gt 10 ]; then
    echo "High 502 count: $COUNT" | mail -s "Nginx 502 Alert" admin@example.com
fi

For production, use proper monitoring tools like Datadog, New Relic, Prometheus with the nginx-exporter, or ELK stack for log analysis.

3. Use Process Supervision

Ensure backend services automatically restart after crashes. For PHP-FPM, systemd handles this by default if enabled. For custom applications:

# systemd service unit with auto-restart
# File: /etc/systemd/system/my-node-app.service
[Service]
ExecStart=/usr/bin/node /opt/app/server.js
Restart=always
RestartSec=5
StartLimitInterval=0

# Or use PM2 with auto-restart
pm2 start app.js --name my-app --watch --max-restarts=10

4. Configure Proper Timeouts System-Wide

Align timeouts across the entire stack: Nginx → Backend → Database → External APIs. A typical chain might look like:

# Nginx: proxy_read_timeout 60s
# Backend application: request timeout 55s
# Database query timeout: 50s
# External API call timeout: 30s

# This cascading timeout pattern ensures graceful failure propagation
# rather than mysterious 502s from upstream closures

5. Maintain Adequate Resource Capacity

Prevent 502s from overload by configuring appropriate limits:

# Nginx worker settings
# File: /etc/nginx/nginx.conf
worker_processes auto;
worker_connections 2048;
worker_rlimit_nofile 65535;

# PHP-FPM pool capacity
pm.max_children = 100
pm.max_requests = 500

# Backend application concurrency
# Node.js: use cluster mode or multiple PM2 instances
# pm2 start app.js -i max
# Gunicorn: --workers 4 --threads 2

6. Keep a Troubleshooting Runbook

Document your specific infrastructure's common 502 causes and fixes. A concise internal runbook saves valuable time during incidents. Include:

7. Use Configuration Management and Version Control

Store Nginx and backend configurations in version control (Git). Use configuration management tools (Ansible, Chef, Puppet) or infrastructure-as-code (Terraform, Docker Compose) to ensure consistency across environments. A misaligned configuration between staging and production is a frequent source of hard-to-diagnose 502 errors.

Quick Reference: Diagnostic Commands Cheat Sheet

# === SERVICE STATUS ===
systemctl status nginx
systemctl status php8.2-fpm
pm2 list

# === PORT & SOCKET CHECKS ===
sudo ss -tlnp | grep -E '80|443|9000|3000'
ls -la /var/run/php/php8.2-fpm.sock

# === LOG TAILING ===
sudo tail -f /var/log/nginx/error.log
sudo journalctl -u php8.2-fpm -f
pm2 logs --lines 50

# === DIRECT BACKEND TEST ===
curl -v http://127.0.0.1:9000
cgi-fcgi -connect /var/run/php/php8.2-fpm.sock /status

# === CONFIGURATION VALIDATION ===
sudo nginx -t
sudo php-fpm8.2 -t     # syntax check PHP-FPM config

# === RESOURCE MONITORING ===
free -h                # memory
htop                   # CPU and process list
ps aux | grep php-fpm | wc -l   # PHP-FPM child count

# === SELINUX CHECKS ===
getenforce
sudo ausearch -m AVC -ts recent | grep nginx

Conclusion

The Nginx 502 Bad Gateway error, while initially intimidating, becomes manageable with a structured diagnostic approach. The key insight is that this error always stems from a breakdown in communication between Nginx and its upstream backend. By methodically checking whether the backend is running, listening on the expected address, responding in time, and returning valid HTTP responses, you can isolate the root cause quickly. The most common fixes involve restarting crashed services, aligning socket permissions, increasing timeout values, expanding buffer sizes, or adjusting backend worker capacity. Implement the preventive measures outlined above — health checks, process supervision, aligned timeouts, monitoring, and configuration management — to minimize the occurrence of 502 errors in production. With this guide as a reference, you'll be equipped to diagnose and resolve the vast majority of 502 Bad Gateway scenarios efficiently and confidently.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles