NGINX 502 BAD GATEWAY: UPSTREAM RETURNED AN INVALID RESPONSE ------------------------------------------------------------------------ nginx reached your application and did not get a usable answer back. The status says nothing about why — that is in nginx's own error log, which names the upstream, the syscall that failed and the reason, and reading it turns guesswork into one lookup. CONFIDENCE : medium VERIFIED : 2026-08-08 (fresh, 0d old) URL : https://knowbase.sh/k/http-502-bad-gateway-nginx ERROR ------------------------------------------------------------------------ 502 Bad Gateway CODES: 502 PROBLEM ------------------------------------------------------------------------ Requests return 502 while nginx itself is running fine. The application may be up, reachable directly on its own port, and showing nothing unusual in its logs. Restarting nginx changes nothing because nginx is not what failed — it is reporting that whatever sits behind it did. ROOT CAUSE ------------------------------------------------------------------------ 1. [primary] The upstream process is not listening Crashed, still starting, or bound to a different address. Binding to 127.0.0.1 while nginx connects to another interface — or the reverse in a container, where 127.0.0.1 is not shared — produces a refused connection. how to tell: The error log says connect() failed (111: Connection refused), and ss -ltnp shows nothing listening on the expected address and port 2. [primary] The upstream took longer than proxy_read_timeout nginx waits 60 seconds by default for a response and then gives up with 502. A slow query or a long export exceeds it while the application is still working normally. how to tell: The error log says upstream timed out, and the elapsed request time in the access log is close to 60 seconds 3. [common] The upstream closed the connection mid-response The worker was killed — OOM, a deploy, or a request timeout inside the application — after nginx had already forwarded the request. nginx receives a truncated response rather than none at all. how to tell: The error log says upstream prematurely closed connection, and the application's own log shows a worker restart or OOM kill at the same timestamp 4. [common] Response headers exceed nginx's buffer A large Set-Cookie or auth header can overflow proxy_buffer_size, and nginx rejects the response as invalid rather than truncating it. how to tell: The error log mentions upstream sent too big header, and the failure follows requests carrying unusually large cookies or tokens 5. [common] The socket exists but nginx cannot use it With a Unix socket, the nginx worker user needs permission on the socket file and every directory above it. SELinux or AppArmor can also deny the connection while permissions look correct. how to tell: The error log reports permission denied on the socket path, or the denial appears in audit.log rather than in nginx's log 6. [edge] Every server in the upstream group is marked down After enough failures nginx takes members out of rotation, and once the last one is out it fails immediately without attempting a connection. how to tell: The error log says no live upstreams, with no connect() attempt recorded SOLUTION ------------------------------------------------------------------------ 1. Read nginx's error log. It names the upstream address, the failing syscall and the errno — which identifies the cause without needing to reproduce anything. $ tail -50 /var/log/nginx/error.log | grep -i upstream note: The distinction between 'Connection refused', 'timed out' and 'prematurely closed' maps directly onto the first three causes; they need different fixes. 2. Check the upstream is listening where nginx is looking. Address mismatches are the single most common cause in containers. $ ss -ltnp | grep -E ':(3000|8000|8080)' ; curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/ 3. For a refused connection, make the bind address and the proxy_pass target agree. In a container, bind to all interfaces rather than loopback. # 🔴 unreachable from another container app.listen(3000, "127.0.0.1"); # ✅ reachable on the container network app.listen(3000, "0.0.0.0"); 4. For a timeout, raise the proxy timeouts only if the request is legitimately slow — otherwise fix the slow path. The default is 60 seconds. location /api/export { proxy_pass http://app; proxy_connect_timeout 5s; # reaching the upstream should be fast proxy_read_timeout 300s; # this endpoint is genuinely slow proxy_send_timeout 300s; } note: Scope generous timeouts to the endpoint that needs them. Raising them globally turns every stuck request into a held worker. 5. For a header-too-big error, give nginx room for the response headers. proxy_buffer_size 16k; proxy_buffers 4 16k; proxy_busy_buffers_size 32k; 6. For premature closes, look at why the worker died rather than at nginx. Check the application's memory ceiling and its own request timeout — nginx is the messenger. VERIFY: The request succeeds, and nginx's error log records no new upstream entries during a full traffic cycle including the slowest endpoint. FALLBACK: While the root cause is being fixed, keep the site partially up by serving a cached or static response for the failing location — proxy_cache_use_stale with error and timeout lets nginx answer from cache instead of returning 502. APPLIES TO ------------------------------------------------------------------------ nginx: all versions (proxy_read_timeout defaults to 60s; proxy_connect_timeout to 60s.) HTTP: RFC 9110 (502 means a gateway received an invalid response from an upstream server.) platforms: linux NOT APPLICABLE TO ------------------------------------------------------------------------ - 504 Gateway Timeout, which some proxies return instead of 502 for the timeout case - 503 Service Unavailable, typically emitted deliberately during maintenance or by rate limiting - 500 from the application itself, which reached nginx as a valid response and is passed through - 413 Request Entity Too Large, which nginx rejects before contacting the upstream at all EVIDENCE ------------------------------------------------------------------------ 1. 502 Bad Gateway https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/502 MDN Web Docs | official-docs | read 2026-08-08 supports: That 502 means a proxy received an invalid response from upstream — so the failure is always behind the proxy, which is why restarting nginx does not help. 2. nginx — ngx_http_proxy_module, proxy_read_timeout https://nginx.org/en/docs/http/ngx_http_proxy_module.html nginx | official-docs | read 2026-08-08 supports: That the default read timeout is 60 seconds and is configurable per location, which is why slow endpoints fail at a consistent one-minute boundary. CONFIDENCE ------------------------------------------------------------------------ medium — The meaning of 502 and the 60-second default read timeout are quoted from MDN and nginx's own module reference, and together they explain the two primary causes. Confidence is medium rather than high because it rests on two sources: the specific error-log strings used as discriminators are nginx's runtime messages rather than documented text, and the buffer sizing values are conventional starting points. ------------------------------------------------------------------------ knowbase 0.1.0 — CC-BY-4.0