What it means: The backend closed the connection before nginx received its complete response, so nginx returned 502 or cut the reply short.
Free online tool — HTTP status code checker: instant results, no signup.
Requests the address and shows the status code and what the server sends back.
nginx logs upstream prematurely closed connection when a read from the backend socket returns end-of-file before the response is complete (ngx_http_upstream.c). The backend did not send a reset; it closed the connection normally, just too early. The text after while is the phase nginx was in, and it decides what the client sees:
| Log ending | Phase | Client sees |
|---|---|---|
while reading response header from upstream | Nothing received yet, or headers incomplete | 502 Bad Gateway, or the response of the next server if proxy_next_upstream allows a retry |
while reading upstream | Headers already forwarded, body in progress | A truncated response; the status was already sent, so nginx can only close the connection |
The second case is why browsers sometimes show ERR_INCOMPLETE_CHUNKED_ENCODING or an empty download instead of a 502. The nginx documentation is explicit: "passing a request to the next server is only possible if nothing has been sent to a client yet" (proxy_next_upstream).
Do not confuse it with recv() failed (104: Connection reset by peer): that is a TCP RST. Here the backend sent a FIN.
| Cause | How to recognise it | Fix |
|---|---|---|
| Worker killed by its own timeout | gunicorn logs WORKER TIMEOUT; its default timeout is 30 s, after which a silent worker is killed and restarted | Speed up the request or raise the app server timeout above the real duration |
| Process crashed or OOM-killed | dmesg -T | grep -i 'killed process', segfaults, container restarts | Fix the crash or give the process more memory |
| PHP-FPM request terminated | FPM log shows a child exited after request_terminate_timeout | Align the FPM limit with fastcgi_read_timeout and the script runtime |
| Keep-alive race | Sporadic, low traffic, with keepalive enabled in the upstream block; the backend closes idle connections just as nginx reuses one | Make the backend's keep-alive timeout longer than nginx's idle reuse, or shorten keepalive_timeout in the upstream block |
| Protocol mismatch | Always fails: proxy_pass http:// to a TLS port, or HTTP to a non-HTTP service | Use the right scheme and port |
| Backend limit on request size or headers | Fails only for large uploads or long cookies | Raise the limit in the app server |
The same log line appears for FastCGI and uwsgi upstreams, because they share the upstream core. A useful habit: the app server's timeout should be longer than the slowest legitimate request, and nginx's proxy_read_timeout (60 s by default, measured between two reads, not for the whole response) should be longer still, so the proxy times out last and logs a clear upstream timed out instead of a confusing early close.
For Node.js, an HTTP server closes idle keep-alive sockets after server.keepAliveTimeout; current releases use 5 seconds, with a change to 65 seconds pending upstream (Node.js http docs). If nginx keeps idle upstream connections longer than the app does, the race above is likely. Set the app's keep-alive timeout explicitly rather than relying on the default of whatever version you run.
grep 'prematurely closed' /var/log/nginx/error.log | tail -5
journalctl -u myapp --since '-10 min' | grep -iE 'timeout|killed|error|exit' A worker timeout or restart at the same second is the answer.curl -sv http://127.0.0.1:8000/slow-endpoint -o /dev/null. curl reports Empty reply from server when the backend closes without answering.keepalive, make sure the location sets proxy_http_version 1.1; and proxy_set_header Connection "";, and that the backend's idle timeout exceeds nginx's.proxy_next_upstream error timeout; is the default; POST requests are not retried unless you add non_idempotent.A general walkthrough of nginx error logs: how to debug nginx errors.
for i in $(seq 1 30); do curl -s -o /dev/null -w '%{http_code} %{size_download}\n' https://example.com/slow-endpoint; done | sort | uniq -c
# 30 200 48213Every response must have the same status and full size; varying sizes mean truncation is still happening. The error log should stay free of prematurely closed. To see what your public URL returns from outside, use the HTTP status checker.
HTTP status codes are three-digit numbers that the server returns for every request. They tell the browser and search crawlers what happened: success, redirect, or error.
Enter a code or keyword — instant result with description.
1xx Informational, 2xx Success, 3xx Redirect, 4xx Client Error, 5xx Server Error.
For each code: typical causes and concrete steps to resolve.
How each code affects indexing and link juice transfer in Google.
choosing correct status
redirect optimization
5xx error diagnostics
landing page check
HTTP monitor alerts when response code changes or 5xx appears.
Sign up freeThe first means the backend closed before nginx had the response headers, so the client gets 502. The second means headers were already sent, so the client gets a truncated response instead of an error page.
No. nginx reports that the backend closed its side of the connection. The cause is in the application, its server or the keep-alive settings between them.
A timeout inside the application stack, such as gunicorn's 30-second worker timeout or PHP-FPM's request_terminate_timeout, kills the worker before it finishes, and nginx sees the connection close.
That pattern points to a keep-alive race: the backend closes an idle connection at the same moment nginx reuses it for a new request.
Only if nginx itself was the one timing out, which logs "upstream timed out" instead. A premature close comes from the backend, so its own timeout or crash has to be fixed.
Free plan — 10 monitors, checks every 5 min, no card required. Upgrade for 1-minute interval and multi-region monitoring.