Skip to main content
502

Bad Gateway

Active
RFC 9110 §15.6.3Since 1997Reverse proxies, CDNs, load balancers, API gateways, service meshes

HTTP 502 Bad Gateway indicates the server, while acting as a gateway or proxy, received an invalid response from an inbound upstream server. Defined in RFC 9110 §15.6.3. The proxy itself is healthy — the upstream origin returned garbage, closed the connection prematurely, or crashed mid-response. Different from 504 which means no response at all.

Description

The 502 Bad Gateway status code indicates that the server, while acting as a gateway or proxy, received an invalid response from an inbound server it accessed while attempting to fulfill the request. The critical distinction: the proxy is operational and listening — but the thing BEHIND it is broken. A response came back from upstream, but it was malformed, incomplete, or violated HTTP protocol semantics.

This is fundamentally different from 504 Gateway Timeout. A 504 means the upstream never responded at all — the proxy waited and gave up. A 502 means a response DID arrive, but it was garbage: malformed HTTP headers, a premature connection close, a response body that contradicts Content-Length, or raw TCP data where HTTP was expected. The proxy cannot forward what it cannot parse.

In Nginx specifically, 502 surfaces when: the upstream process crashes mid-response (connection reset by peer — the single most common cause in nginx error logs), the upstream returns headers larger than proxy_buffer_size, the upstream sends malformed HTTP that fails header parsing, or the FastCGI/uwsgi process terminates unexpectedly. The proxy_buffer_size default of 4k/8k is a frequent silent trigger — a single large Set-Cookie header or an application dumping debug output into response headers can push past this limit.

Cloudflare returns a branded 502 page when the origin server is completely down or returns an invalid HTTP response. AWS ALB returns 502 when: the target closed the connection before sending a complete response, the target response was malformed, or the target response body exceeded the connection timeout. In all cases, the fix pattern is the same: check upstream server health, not the proxy itself. The proxy is the messenger — it's faithfully reporting that your backend is broken.

The most insidious 502 scenarios are intermittent ones caused by connection pool exhaustion, keep-alive timeouts mismatched between proxy and upstream, or garbage collection pauses in JVM-based backends that cause partial response writes. These appear random, vanish under light load, and only manifest at scale — making them notoriously difficult to reproduce in development environments.

Examples

Request through Nginx reverse proxy
http
GET /api/users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
502 response — upstream connection reset
http
HTTP/1.1 502 Bad Gateway
Content-Type: text/html; charset=utf-8
X-Request-Id: req_7f3a2b1c
X-Upstream-Status: failed
Retry-After: 5

<html>
<head><title>502 Bad Gateway</title></head>
<body>
<h1>502 Bad Gateway</h1>
<p>The upstream server terminated the connection unexpectedly.</p>
<p>Request ID: req_7f3a2b1c</p>
</body>
</html>
POST request to API behind load balancer
http
POST /api/orders HTTP/1.1
Host: shop.example.com
Content-Type: application/json
X-Request-Id: ord_9e4f2a8d

{"items": [{"sku": "WIDGET-42", "qty": 3}], "shipping": "express"}
502 response — upstream returned malformed headers
http
HTTP/1.1 502 Bad Gateway
Content-Type: application/json
X-Request-Id: ord_9e4f2a8d
X-Cache: MISS

{"error": "bad_gateway", "message": "The upstream server returned an invalid HTTP response. This request was not processed — it is safe to retry.", "request_id": "ord_9e4f2a8d", "upstream": "order-service:8080", "retry_after_seconds": 3}
WebSocket upgrade through proxy — upstream crashed
http
GET /ws/live-feed HTTP/1.1
Host: stream.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
502 response — WebSocket backend unavailable
http
HTTP/1.1 502 Bad Gateway
Content-Type: text/plain
Connection: close

WebSocket backend at ws-cluster:9090 returned an invalid response during upgrade handshake. The connection was reset by the upstream server.

Edge Cases

  • 502 means the PROXY is healthy but the UPSTREAM is broken. Always check upstream server logs first — the proxy is just the messenger reporting that your backend returned garbage.
  • Nginx 'connection reset by peer' is the #1 cause of 502 in production. It means the upstream process (PHP-FPM, Node.js, Python) crashed or was killed (OOM, segfault, SIGKILL) while writing the response. Check dmesg and upstream process logs.
  • proxy_buffer_size in Nginx defaults to 4k/8k. If upstream response headers exceed this (large Set-Cookie, debug headers, CORS headers with many origins), Nginx returns 502 without any upstream error. Fix: increase proxy_buffer_size and proxy_buffers.
  • Keep-alive timeout mismatch: if upstream closes idle connections after 60s but the proxy assumes they're alive for 120s, requests sent on stale connections get 502. Fix: set proxy_read_timeout < upstream's keep-alive timeout, or disable keep-alive to upstream.
  • Intermittent 502 under load often indicates upstream connection pool exhaustion. The proxy opens connections faster than the upstream can accept them. The upstream's listen backlog fills, new SYNs are dropped, and the proxy sees a reset. Fix: increase upstream worker count, tune somaxconn.
  • AWS ALB returns 502 if the target responds with a Content-Length header that doesn't match the actual body length, or if the target sends a chunked response with invalid chunk encoding. This is common when application frameworks crash mid-response.
  • Cloudflare 522 (not 502) means TCP connection to origin timed out. Cloudflare 502 specifically means the origin DID respond but the response was invalid HTTP. If you see Cloudflare's branded error page, it's their 502. If you see your own error page through Cloudflare, that's the origin returning 502.
  • GC pauses in JVM backends (Java, Kotlin, Scala) can cause 502: the proxy sends a request, the JVM enters a stop-the-world GC, the connection idles past the proxy timeout, and the proxy returns 502. The request may have been partially processed. This is especially dangerous for non-idempotent operations.
  • Docker/Kubernetes: 502 during deployment usually means the old pod was killed before draining connections. Fix: add preStop lifecycle hook with sleep, configure terminationGracePeriodSeconds, ensure readiness probes fail before pod termination.

When You'll See This

  • Upstream application server crashed (OOM kill, unhandled exception, segfault) — connection reset by peer in proxy logs
  • Backend microservice returned malformed HTTP headers (missing status line, invalid header syntax, headers exceeding buffer size)
  • Origin server process was restarted during a deployment and existing connections were severed mid-response
  • FastCGI/PHP-FPM worker hit max_execution_time or memory_limit and was terminated by the process manager
  • Upstream responded with HTTP/0.9 or raw non-HTTP data on an HTTP connection (common when accidentally routing to a non-HTTP service port)
  • DNS resolution for the upstream host failed or returned a stale IP pointing to a decommissioned server
  • SSL/TLS handshake failure between proxy and upstream (expired certificate, cipher mismatch, SNI not configured)
  • Upstream's response exceeded Content-Length or used invalid chunked transfer encoding, causing the proxy to reject it as malformed

Implementation References

LanguageConstant
Gohttp.StatusBadGateway
Rusthttp::StatusCode::BAD_GATEWAY
Pythonhttp.HTTPStatus.BAD_GATEWAY
Node.jshttp.STATUS_CODES[502]
.NETHttpStatusCode.BadGateway
JavaHttpURLConnection.HTTP_BAD_GATEWAY
PHPResponse::HTTP_BAD_GATEWAY (Symfony)
Ruby:bad_gateway (Rack)

History

Introduced in HTTP/1.1 (RFC 2068, January 1997) as part of the original 5xx server error class. Carried forward unchanged through RFC 2616 (1999) and RFC 7231 (2014), now canonically defined in RFC 9110 (June 2022) §15.6.3. Became one of the most commonly encountered errors with the universal adoption of reverse proxies (Nginx, HAProxy), CDNs (Cloudflare, Fastly, Akamai), and microservice architectures where every request traverses multiple proxy layers.

Related Status Codes

Related Headers

FAQ

What is the difference between 502 Bad Gateway and 504 Gateway Timeout?

502 means the proxy received a response from upstream but it was invalid (malformed HTTP, connection reset mid-response, garbage data). 504 means the proxy received NO response at all — the upstream was silent and the proxy's timeout expired. Think of it this way: 502 = 'upstream said something, but it was nonsense.' 504 = 'upstream said nothing at all.' Both indicate upstream problems, but the debugging path differs — 502 points to crashes, malformed output, or buffer issues; 504 points to hangs, deadlocks, or network partitions.

How do I fix Nginx 502 Bad Gateway errors?

First, check the Nginx error log (usually /var/log/nginx/error.log) for the specific upstream failure. The most common causes and fixes: (1) 'connection reset by peer' — your upstream process crashed; check if it was OOM-killed via dmesg or journalctl. (2) 'upstream sent too big header' — increase proxy_buffer_size to 16k or 32k. (3) 'connect() failed — Connection refused' — your upstream isn't running or is listening on the wrong port/socket. (4) 'no live upstreams' — all servers in the upstream block failed health checks. For intermittent 502s under load, check upstream worker count, connection pool limits, and keep-alive timeout alignment between Nginx (proxy_read_timeout) and your upstream server.

Is it safe to retry a request that got a 502 response?

For GET and other idempotent methods (HEAD, OPTIONS, PUT, DELETE) — yes, retrying is safe and recommended. The 502 confirms the proxy didn't forward a valid response, so the request likely wasn't processed. For POST and other non-idempotent methods — it depends. If the upstream crashed BEFORE processing (connection refused), retry is safe. If it crashed DURING processing (connection reset mid-response), the operation may have partially completed. Best practice: use idempotency keys for non-idempotent operations, implement exponential backoff with jitter (start at 1s, cap at 30s), and respect Retry-After headers if present in the 502 response.