HTTP 502 Bad Gateway — What It Means and How to Fix It
502 Bad Gateway
Verified against RFC 9110 §15.6.3 (HTTP Semantics), Nginx docs: ngx_http_upstream_module, AWS ALB troubleshooting guide · Updated June 2026
> quick_fix
Your reverse proxy (Nginx, AWS ALB, Cloudflare) couldn't get a valid response from the backend server. Check that the backend is running and listening on the correct port. Most 502s resolve by restarting the crashed backend process.
# Is the backend actually running?
ps aux | grep node
curl -si http://localhost:3000/health # test backend directly, bypassing proxy
# Check if it's listening on the right port
ss -tlnp | grep :3000
lsof -iTCP:3000 -sTCP:LISTENWhat causes this error
HTTP 502 is a proxy-layer error meaning the reverse proxy successfully connected to the upstream server but received a response that was invalid, malformed, or an abrupt connection close (crash). Common causes: the backend process crashed and isn't listening, the backend is starting up and not yet ready, the wrong port is configured in the proxy, the backend sent an invalid HTTP response, or the backend hit an OOM and was killed by the OS.
How to fix it
- 01
step 1
Test the backend directly, bypassing the proxy
If the backend is reachable directly but the proxy gives 502, the issue is in the proxy configuration. If the backend is unreachable directly, the issue is the backend itself — crashed or not listening.
# Test backend directly (on the server machine, bypassing Nginx/ALB) curl -si http://localhost:3000/health curl -si http://127.0.0.1:8080/ # If this works but public URL gives 502 → proxy misconfiguration # If this fails → backend is down - 02
step 2
Check and restart the backend process
A crashed Node.js, Python, or Go process leaves nothing listening on the port. The proxy correctly reports 502. Use your process manager to check status and restart.
# PM2 pm2 status pm2 restart my-app pm2 logs --err --lines 50 # systemd systemctl status my-service systemctl restart my-service journalctl -u my-service -n 100 -f # Docker docker ps -a # check if container exited docker logs <container_id> --tail 50 docker restart <container_id> - 03
step 3
Verify the proxy's upstream port matches the backend
A common misconfiguration: the proxy is pointing at port 3000 but the app was changed to listen on port 8080 (or vice versa). Check both the proxy config and the app's PORT environment variable.
# Check Nginx upstream config cat /etc/nginx/sites-enabled/myapp # upstream backend { server 127.0.0.1:3000; } ← must match app's listening port # Check what port the app is actually on ss -tlnp | grep node - 04
step 4
Check for OOM kills
The Linux OOM killer terminates processes when memory is exhausted — it leaves no application-level log. Check `dmesg` for OOM kill entries.
# Check for OOM kills dmesg | grep -i 'oom\|killed' | tail -20 # Check current memory usage free -h cat /proc/meminfo | grep MemAvailable - 05
step 5
For AWS ALB/Cloudflare: check backend health check status
AWS ALB removes unhealthy targets from the pool and returns 502 when no healthy targets remain. Check the target group's health check in the AWS console. Cloudflare 502 means your origin is unreachable or returning an invalid response.
# AWS — check target health from CLI aws elbv2 describe-target-health \ --target-group-arn arn:aws:elasticloadbalancing:...:targetgroup/... # Cloudflare — check if origin responds directly curl -si https://your-origin-ip/health -H 'Host: yourdomain.com'
How to verify the fix
- Backend health endpoint returns 200 directly (bypassing proxy).
- Public URL returns a 2xx response.
- ALB target group shows all targets as Healthy.
Why 502 happens at the runtime level
HTTP 502 is generated by the proxy layer (Nginx, HAProxy, AWS ALB, Cloudflare) when it successfully opens a connection to the upstream server but receives a response that violates HTTP protocol — or when the upstream closes the connection before sending a complete response. At the TCP level: the proxy connects, sends the HTTP request, and either gets a RST (connection reset — the upstream crashed mid-response), an incomplete response (connection closed before `Content-Length` bytes arrived), or a response that fails HTTP parsing (malformed status line, invalid headers). The proxy synthesizes a 502 because it cannot forward an invalid response to the client.
Common debug mistakes for 502
- New deployment starts listening on a different port than what Nginx/ALB expects, causing all requests to fail immediately.
- Node.js app crashes on startup due to a missing environment variable — the process manager doesn't restart fast enough before health checks fail.
- Long-running backend operation closes the connection before the proxy's `proxy_read_timeout` — Nginx emits 504, not 502, but some confuse the two.
- Backend app sends HTTP/2 responses but the proxy is configured for HTTP/1.1 upstream — the protocol mismatch causes 502.
- In Docker: container is 'Up' but the health check inside hasn't passed yet — ALB still routes to it and gets 502 from a half-initialized app.
When 502 signals a deeper problem
Repeated 502s during deployments signal missing zero-downtime deployment infrastructure. Every deploy that takes the app down for even 1 second causes 502s for real users. The fix requires a graceful shutdown protocol: the server must stop accepting new connections, drain in-flight requests (wait for them to complete), then exit. In Node.js this is `server.close()` + `process.exit()` inside a SIGTERM handler. Kubernetes implements this via `terminationGracePeriodSeconds`. Combined with a rolling deploy strategy, this eliminates deployment-caused 502s entirely.
Editor's take
The 502 is the most common error associated with deployment pipelines and infrastructure changes. It has a distinctive pattern: a spike that starts exactly when a deployment completes and drops when the new process stabilizes. If your monitoring shows this pattern, you're doing hot-swaps without a grace period. The PM2 `reload` command, Kubernetes rolling updates, and blue-green deploys all exist to prevent this specific pattern.
The version of this error that wastes the most debugging time: a Cloudflare 502 where the origin is actually healthy. Cloudflare validates the origin's SSL certificate in Full (strict) mode — an expired or self-signed cert causes a 502 that looks identical to an origin outage. The tell is checking Cloudflare's Ray ID in the response headers (`CF-Ray`), then looking up that Ray ID in Cloudflare's dashboard which shows the exact reason for the error. Most developers don't know this lookup is possible and spend an hour checking their application when the problem is a 90-day cert that rolled over.
The adjacent error pattern in Kubernetes: a pod is Running but fails its readiness probe because the health check endpoint isn't ready yet. The pod gets traffic from the Service, returns 502 from its not-yet-ready state, and the readiness probe eventually passes — but not before 30 seconds of 502s hit real users. Fix: implement the health check endpoint first, before the rest of the app initializes, and have the readiness probe check a more detailed state (DB connected, migrations run, cache warm) rather than just 'is the process alive.'
By Bikram Nath · Curator · Updated June 2026
Frequently asked questions
What is the difference between 502 and 503?
502 (Bad Gateway) means the proxy got an invalid or no response from the upstream server — the upstream is likely crashed or misconfigured. 503 (Service Unavailable) means the server knows it can't handle the request right now — often returned deliberately when the server is overloaded or in maintenance mode, and usually includes a `Retry-After` header.
Why do I see 502 immediately after a deployment?
During a deployment, the old process is killed and the new one starts. There's a window (typically 1-10 seconds) where nothing is listening on the port. If requests arrive during this window, they get 502. Fix this with a graceful deployment strategy: start the new process, wait for it to pass health checks, then kill the old one (blue-green, rolling deploy). PM2's `reload` command does this for Node.js without a gap.
Cloudflare shows a 502 but my origin server shows no errors — why?
Cloudflare generates its own 502 error page when it can't reach your origin at all (DNS timeout, origin offline) or when the origin sends an invalid HTTP response (incomplete headers, response truncated mid-stream). The 502 never reaches your origin server, so there's nothing in your app logs. Check Cloudflare's dashboard → Analytics → Errors for the origin-unreachable reason. Also check if your origin's SSL certificate is valid — Cloudflare's 'Full (strict)' SSL mode validates the origin cert and gives 502 on cert errors.