Skip to content
← All articles

HTTP 502 Bad Gateway: What It Means and How to Fix

The 502 Bad Gateway error means a proxy server (nginx, Apache, Cloudflare) did not receive a valid response from the upstream server (PHP-FPM, Node.js, uwsgi, Gunicorn). It is not an application code problem — it is a failure between the proxy and the backend.

This article covers what 502 means across different stacks, 7 common causes, and step-by-step fixes for nginx + PHP-FPM, Node.js, and Apache with config examples.

What 502 Means

Per RFC 9110 §15.6.3, 502 indicates that the server, acting as a gateway or proxy, received an invalid response from upstream. Unlike 504 (timeout), 502 is a syntactic or connection failure: upstream crashed, returned garbage, or reset the connection.

Request Flow and Where 502 Arises

Browser → CDN/Cloudflare → nginx → PHP-FPM / Node.js / uwsgi
                            ↑
                            502 from this hop if upstream:
                            - does not respond
                            - returns invalid HTTP
                            - closes the connection
                            - connect() times out

7 Common Causes

  1. PHP-FPM / Node.js crashed or is not running.
  2. Worker pool exhausted — all children busy.
  3. OOM Killer terminated the process — system out of RAM.
  4. Upstream connect() timeout.
  5. Broken socket (unix socket deleted or permission issue).
  6. Deploy in progress — upstream restarting.
  7. Oversized headers — buffer overflow.

Diagnosing 502

Check externally via Enterno.io HTTP Header Checker, then dig into server logs.

# Process status
systemctl status php8.4-fpm
systemctl status nginx
pm2 list  # for Node.js

# Logs
tail -100 /var/log/nginx/error.log
tail -100 /var/log/php8.4-fpm.log

# Check OOM Killer
dmesg | grep -i "killed process\|out of memory"
journalctl --since "10 minutes ago" | grep -i oom

# Check socket
ls -la /var/run/php/php8.4-fpm.sock
# Expected: srw-rw---- 1 www-data www-data

Solutions

1. Restart upstream

systemctl restart php8.4-fpm
systemctl restart nginx
# or PM2
pm2 restart all

2. Increase PHP-FPM worker pool

# /etc/php/8.4/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500

# For high load:
pm = static
pm.max_children = 100
pm.max_requests = 1000

systemctl reload php8.4-fpm

Correct pool size: max_children = (RAM - 500MB) / avg_worker_MB. Monitor via pm.status_path = /fpm-status.

3. Tune nginx buffers and timeouts

http {
    # Increase if upstream sends large headers
    fastcgi_buffer_size 128k;
    fastcgi_buffers 8 128k;
    fastcgi_busy_buffers_size 256k;

    # Extend timeout for slow upstream
    fastcgi_connect_timeout 30s;
    fastcgi_send_timeout 60s;
    fastcgi_read_timeout 60s;

    # For proxying to Node.js
    proxy_buffer_size 128k;
    proxy_buffers 8 128k;
    proxy_busy_buffers_size 256k;
    proxy_connect_timeout 30s;
    proxy_read_timeout 60s;
}

4. Check and fix the socket

# If using unix socket
listen = /var/run/php/php8.4-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

# Alternative — TCP (fewer permission issues):
listen = 127.0.0.1:9000

5. OOM protection

# Raise memory_limit carefully
memory_limit = 256M

# Swap file (for low-RAM VPS)
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo "/swapfile none swap sw 0 0" >> /etc/fstab

# OOM priority for critical processes
systemctl edit php8.4-fpm
# [Service]
# OOMScoreAdjust=-500

6. Graceful zero-downtime deploy

# Blue-green via nginx
upstream backend {
    server 127.0.0.1:3000;  # blue
    server 127.0.0.1:3001 backup;  # green
}

# Reload instead of restart
systemctl reload php8.4-fpm  # no downtime
pm2 reload all  # zero-downtime restart

7. Node.js specifics

// Unhandled rejection = process crash = 502
process.on('unhandledRejection', (reason) => {
  logger.error('Unhandled Rejection:', reason);
  // Do not exit — keep running
});

process.on('uncaughtException', (err) => {
  logger.error('Uncaught Exception:', err);
  // Strategy: log and continue (or graceful shutdown)
});

// PM2 for auto-restart
// pm2 start app.js --max-memory-restart 500M

502 from Cloudflare — What to Do

If Cloudflare shows 502 with a cf-ray ID:

  1. Verify origin responds directly (bypass CF: curl -H 'Host: example.com' http://ORIGIN_IP).
  2. Check the SSL certificate on origin — expired = 502 from CF.
  3. Ensure origin does not block CF IP ranges (WAF, firewall).
  4. Check CF Workers — errors in worker code return 502.

Monitoring and Prevention

Configure Enterno.io Monitors with expected_code=200 and regions ru-msk + eu-de — detect 502 instantly even if it only appears from specific geos. Hook up Telegram or Slack for immediate alerts.

Frequently Asked Questions

Q: How does 502 differ from 504?
A: 502 — upstream returned garbage or closed the connection. 504 — upstream did not respond in time.

Q: Why does deploy cause 502?
A: Upstream is restarting. Solution: graceful reload instead of restart + zero-downtime deploy via PM2/systemd.

Q: 502 only sometimes — what to check?
A: Worker pool, OOM Killer, timeouts on slow requests, socket state.

Conclusion

502 is a problem between proxy and backend. Always start by checking upstream process status, then logs. Size your worker pool, buffers, timeouts, and OOM protection correctly. Monitoring via Enterno.io catches regressions instantly.

Check your website right now

Check now →
More articles: HTTP
HTTP
HTTP 504 Gateway Timeout: Causes and Solutions for Sysadmins
15.04.2026 · 7 views
HTTP
HTTP Status Codes: Complete Reference with Examples
10.03.2025 · 64 views
HTTP
HTTP 404 Not Found Error: 7 Causes and How to Fix
15.04.2026 · 10 views
HTTP
HTTP 429 Too Many Requests: Rate Limiting Explained
15.04.2026 · 7 views