< HTTP/1.1 502 Bad GatewayHTTP 502 Bad Gateway: nginx, ALB and Cloudflare Fixes
Fix 502 Bad Gateway: decode nginx error-log lines, php-fpm sockets, ALB keep-alive mismatches and Cloudflare 502 vs 52x, with curl checks.
- Cacheable
- Only with explicit freshness
- Retry?
- Yes for idempotent requests, with backoff
- Usually sent by
- Reverse proxy, load balancer or CDN
- Spec
- RFC 9110 §15.6.3
On this page
TL;DR: HTTP 502 Bad Gateway means a proxy could not get a usable response from the server behind it. The error log of the proxy names the exact failure; match the line to the fix below, and check that the upstream process is running, on the expected port or socket, and not closing connections early.
What it means
RFC 9110 section 15.6.3: a gateway or proxy received an invalid response from an inbound server it accessed while attempting to fulfil the request. In practice that covers a refused connection, a connection reset, a crashed worker, a malformed response, or oversized response headers. If the proxy’s upstream timeout expires, it can return 504 instead.
Server responses and client messages below are illustrative.
HTTP/1.1 502 Bad Gateway
Server: nginx
Content-Type: text/html
What you see in your client
These messages assume the response reason is Bad Gateway; a server can use different wording.
- Axios:
AxiosError: Request failed with status code 502. With the defaultvalidateStatus, Axios rejects this response. Inspecterror.response.status,error.response.headersanderror.response.databefore changing the request. Axios source. - fetch: the promise resolves with
response.status === 502andresponse.ok === false. Checkokyourself; acatchblock alone misses HTTP error responses. Fetch behaviour. - Python requests:
requests.exceptions.HTTPError: 502 Server Error: Bad Gateway for url: https://example.com/api. This appears when you callresponse.raise_for_status(). Save the response body and headers before raising. Requests source. - curl -f:
curl: (22) The requested URL returned error: 502. Usecurl -sS -D - https://example.com/apiwhile diagnosing so you retain the error body. curl source. - .NET:
System.Net.Http.HttpRequestException: Response status code does not indicate success: 502 (Bad Gateway).This is the English message fromEnsureSuccessStatusCode()with that reason phrase. InspectStatusCodeand read the body before calling it. Runtime source, message resource. - Spring WebClient:
org.springframework.web.reactive.function.client.WebClientResponseException$BadGateway: 502 Bad Gateway from GET https://example.com/api.retrieve()uses this subclass by default; readgetResponseBodyAsString(). RestTemplate usesHttpServerErrorException.BadGatewayfor this 5xx status. WebClient source, RestTemplate handler.
Who sent it?
| Signal | Layer |
|---|---|
Server: nginx, page “502 Bad Gateway” | nginx; read its error.log |
Server: awselb/2.0 | ALB; check target health and keep-alive settings |
Server: cloudflare + CF-Ray, Cloudflare-styled page “Bad gateway” | Cloudflare saw a bad response from your origin or origin proxy |
X-Cache: Error from cloudfront | CloudFront |
| Body from your own app server | The app returned 502 itself (an upstream API call failed) |
curl -sI https://example.com/ | grep -iE '^(HTTP|server|via|cf-ray|x-cache|x-amz-cf-id|x-amzn)'
curl -sI http://127.0.0.1:3000/ # bypass the proxy: hit the app directly on the box
If the direct request works but the proxied one fails, the problem is in the proxy config or the connection between them.
Fix it: match the nginx error log
Run tail -f /var/log/nginx/error.log and reproduce the 502.
| Log line | Meaning and fix |
|---|---|
connect() failed (111: Connection refused) while connecting to upstream | Nothing is listening at the proxy_pass address. Start the app, fix the port, or check you used 127.0.0.1 vs localhost (check whether the selected address is IPv4 or IPv6). |
connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory) | The PHP-FPM version or socket path in fastcgi_pass is wrong. |
connect() to unix:/run/php/php8.3-fpm.sock failed (13: Permission denied) | The nginx user cannot access the socket: set listen.owner/listen.group/listen.mode in the FPM pool to match nginx’s user. |
upstream prematurely closed connection while reading response header from upstream | The app crashed, was OOM-killed, hit a worker timeout (gunicorn --timeout), or closed an idle keep-alive connection nginx reused. Check the app log and dmesg for OOM kills. |
recv() failed (104: Connection reset by peer) while reading response header from upstream | Same family: the upstream reset the socket. |
upstream sent too big header while reading response header from upstream | Response headers (often large Set-Cookies) exceed the buffer: raise proxy_buffer_size (see below). |
no live upstreams while connecting to upstream | nginx has no available peer in the upstream group; inspect servers marked down and passive failure tracking (max_fails/fail_timeout). Restore the backends and inspect max_fails/fail_timeout. |
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Connection ""; # needed for upstream keepalive
proxy_set_header Host $host;
proxy_buffer_size 16k; # default 4k or 8k; holds the response headers
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
}
upstream app {
server 127.0.0.1:3000;
keepalive 32;
}
For PHP-FPM, fastcgi_buffer_size 16k; fastcgi_buffers 16 16k; is the equivalent. If a 502 appears only after the app has been idle, check for a keep-alive race: the upstream timeout is shorter than the time nginx keeps the connection pooled. Align them (see Node below) or drop keepalive.
Fix it: other stacks
Use the proxy log to distinguish a refused connection from a malformed response; both can produce 502.
Common causes by stack
AWS ALB and Node.js
ALB closes idle connections at 60s by default. If the target closes its idle connection first, the ALB can return 502. Inspect the access-log fields elb_status_code and target_status_code to locate the responding layer. The target must keep connections open longer than the ALB:
const server = app.listen(3000)
server.keepAliveTimeout = 65_000 // greater than ALB idle timeout (60s)
server.headersTimeout = 66_000 // separate limit for receiving request headers
Node’s headersTimeout governs incoming request headers, separately from idle keep-alive. Gunicorn’s --keep-alive applies to workers that support persistent connections; its sync worker ignores it. Node HTTP settings, Gunicorn settings. Other ALB 502 causes: the target sent a malformed HTTP response or a header over the limit, a TLS handshake failure to HTTPS targets, or the target is a Lambda that returned an invalid response shape. ALB troubleshooting.
API Gateway with Lambda: a function error or a wrongly formatted Lambda proxy response produces 502 with {"message":"Internal server error"}. Inspect Lambda logs and the response contract (statusCode, string body, headers). A rejected Lambda invocation produces 500 instead. AWS Lambda integration errors.
Cloudflare
Cloudflare 502 and 504 can come from your origin or from Cloudflare. Cloudflare’s diagnosis guide distinguishes branded origin errors from unbranded Cloudflare errors. If an origin or its proxy answered 502, fix that layer; if the Cloudflare page says “Bad gateway” with a Ray ID, check origin logs for that time, and look at the 52x codes first, since they are more specific: 520 (empty or invalid response), 521 (refused), 522 (connection timeout), 523 (unreachable), 524 (timeout after connect).
Kubernetes and PaaS
In ingress-nginx, connection refusal or a pod dropping a connection can produce 502. Compare the logged upstream address with the Service’s EndpointSlices and listening port. Check kubectl logs and kubectl describe pod, make readiness probes accurate, and allow in-flight requests to finish within the termination grace period. Kubernetes lifecycle hooks. Heroku router errors H12 (request timeout) and H13 (connection closed without response) use 503. Heroku error codes.
Application as the proxy
If your own code calls another service and returns 502 when that call fails, log the upstream URL, status and error, and set client timeouts; do not pass the upstream’s raw error body through to browsers.
A 502 from a localhost API gateway
If the failing URL is http://127.0.0.1:15721/v1/responses, inspect that local gateway’s log and configured upstream first. Make a direct request to the actual upstream from the same host or container. 127.0.0.1 points to the caller’s own network namespace; inside a container, use the reachable service address. Compare status, response body and timestamp across both hops. Raising an HTTP timeout will not start a missing listener or repair an invalid upstream response.
Related codes
- 503 Service Unavailable: no healthy upstream, or deliberate unavailability.
- 504 Gateway Timeout: a gateway timed out connecting or waiting for an upstream.
- 500 Internal Server Error: the responding server failed unexpectedly.
- 520 Web Server Returned an Unknown Error: Cloudflare’s empty or invalid origin response.
Step-by-step nginx fixes: nginx 502 Bad Gateway: read the error log, fix the upstream.
See also the comparison 502 vs 503 vs 504: how to tell the three gateway errors apart from the nginx error log and Cloudflare codes.
Frequently asked questions
What does 502 Bad Gateway mean?
A server acting as a gateway or proxy (nginx, a load balancer, a CDN) received an invalid response, or no usable response, from the upstream server it forwarded the request to. Check the upstream connection and response format as well as the proxy configuration.
How do I fix nginx 502 Bad Gateway?
Read /var/log/nginx/error.log. "Connection refused" means the upstream is down or listening on a different port or socket; "prematurely closed connection" means it crashed or closed the connection early; "upstream sent too big header" means you need a larger proxy_buffer_size; "Permission denied" on a unix socket means a file permission mismatch.
Why does my AWS ALB return 502 intermittently?
Commonly because the target closes idle keep-alive connections before the ALB does. The ALB idle timeout defaults to 60 seconds. Set the backend keep-alive timeout longer than the configured ALB idle timeout; for an unchanged ALB, server.keepAliveTimeout = 65000 is one Node.js setting. Inspect ALB access logs to distinguish a target response from an ALB-generated error.
What is the difference between 502 and 504?
502 means the proxy got a bad or no response from the upstream, such as a refused connection or a connection reset. 504 means the gateway timed out waiting for its upstream; that can happen while connecting or while waiting for a response.
Why does a localhost API gateway return 502?
Inspect the local gateway log and configured upstream, then test that upstream from the same host or container. A refused port, reset connection or invalid response needs a gateway or backend fix. In a container, 127.0.0.1 reaches that container rather than another service.
Is a 502 error caused by my browser?
Almost never. It is a server-side failure between servers, so clearing cache or cookies rarely helps. As a visitor, retry after a minute; as the site owner, debug the upstream.
How is Cloudflare 502 different from 520 and 521?
A 502 means the origin or an upstream proxy returned an invalid response to Cloudflare, or Cloudflare itself hit an internal problem. 520 is an empty or unrecognizable response, 521 means the origin refused the connection, and 522 means the connection timed out; those Cloudflare-specific codes are more precise, so check which one you actually see.
Sources
Related
502 vs 503 vs 504: Which Gateway Error Is It
502, 503 and 504 side by side: who generated each one, what the upstream did, the nginx error-log line that proves it, and the Cloudflare 52x and ALB equivalents.
508 Loop Detected: Proxy and Redirect Loops
508 Loop Detected means the server found an infinite loop while processing a request. Learn the WebDAV origin, proxy loops, CDN-Loop and how to find the cycle.
511 Network Authentication Required: Captive Portals
511 is returned by a network gateway, such as hotel or airport Wi-Fi, that requires login before granting internet access. Learn how apps should handle it.
521 Web Server Is Down
Cloudflare-specific status code indicating the origin server refused the connection. Learn about this proxy error and how to troubleshoot it.
nginx 502 Bad Gateway: Causes and Fixes by Error Log
Fix nginx 502 Bad Gateway by matching the error log: connection refused, prematurely closed connection, php-fpm socket permissions, too big header, keepalive.