HTTP 502 is Bad Gateway: a server acting as a gateway or proxy got an invalid response from the server behind it (RFC 9110 section 15.6.3). In practice “invalid” includes no response at all: a refused connection, a connection closed mid-request, bytes that aren’t HTTP. The proxy is working; the thing behind it isn’t.
Where to look
The proxy that sent the 502 logs why. In nginx’s error log:
connect() failed (111: Connection refused) while connecting to upstream
upstream prematurely closed connection while reading response header from upstream
upstream sent too big header while reading response header from upstream
connect() to unix:/run/php/php-fpm.sock failed (2: No such file or directory)
In order: nothing listens on that port; the app crashed or was killed mid-request; its response headers, often big cookies, overflowed proxy_buffer_size; PHP-FPM isn’t running. A Gunicorn worker killed by its own timeout, 30 seconds by default, shows up as the second line.
In the cloud
- AWS ALB: the target closed the connection or sent a malformed response. The classic cause is keep-alive: Node’s
server.keepAliveTimeoutdefaults to 5 seconds and the ALB’s idle timeout to 60, so the ALB reuses a connection the app has just closed. Make the app’s keep-alive timeout longer than the load balancer’s. - CloudFront: it couldn’t complete the TLS handshake with the origin, often because the origin’s certificate doesn’t cover the origin domain name, or the origin’s response was invalid.
- Kubernetes ingress-nginx: pods stopped during a rollout before they left the Service’s endpoints, or a Service whose
targetPortdoesn’t match the port the container listens on. A shortpreStopsleep lets traffic drain before the process exits. - Go’s
httputil.ReverseProxyanswers 502 for any upstream error, timeouts included, unless you set itsErrorHandler.
502, 503 or 504
the upstream answered badly, or hung up 502 Bad Gateway
temporarily unable to serve: overload, no pods 503 Service Unavailable
the upstream didn't answer in time 504 Gateway Timeout
Your application shouldn’t send 502 for its own bugs; that’s 500. The code is for proxies, and for services that pass on a failure from the service behind them.
What the client should do
Retry idempotent requests with backoff; a 502 during a deploy usually clears within seconds. For a POST, check whether the work happened before sending it again. 502 isn’t heuristically cacheable, though CloudFront caches it for 10 seconds by default. Googlebot slows down on 5xx errors, and pages that keep failing drop out of the index.
In your stack
location / {
proxy_pass http://127.0.0.1:3000;
proxy_buffer_size 16k; # room for large response headers
proxy_next_upstream error timeout; # the default: on a failed connection, try the next server
}
const server = app.listen(3000);
server.keepAliveTimeout = 65_000; // longer than the ALB's 60-second idle timeout
server.headersTimeout = 66_000; // and this a little longer still
lifecycle:
preStop:
exec:
command: ["sleep", "5"]