What it means

nginx writes this line to its error.log when it proxies a request (proxy_pass, but also uwsgi_pass and fastcgi_pass) and the upstream closes the TCP connection before sending a complete response header. nginx has nothing to forward, so it returns 502 Bad Gateway.

[error] 1234#1234: *5678 upstream prematurely closed connection while reading response header from upstream, client: 203.0.113.7, server: example.com, request: "GET /api/report HTTP/1.1", upstream: "http://127.0.0.1:3000/api/report"

The request and upstream fields tell you which route and which backend to look at.

Common causes

1. The worker was killed mid-request. A crash, an unhandled exception that exits the process, or the Linux OOM killer. Confirm:

sudo dmesg -T | grep -iE "killed process|out of memory"
journalctl -u myapp --since "15 min ago"

If the kernel killed it, see how to find what the OOM killer ended.

2. The application server's own timeout fired. Gunicorn kills a worker that stays silent longer than --timeout (30 seconds by default) and logs WORKER TIMEOUT; PHP-FPM does the same with request_terminate_timeout. Confirm:

journalctl -u gunicorn --since "1 hour ago" | grep -i "worker timeout"

3. A keep-alive race. With keepalive in the upstream block, nginx reuses idle connections. If the backend closes them sooner (Node.js server.keepAliveTimeout is 5 seconds by default), nginx can send a request on a socket the backend is closing. The errors are rare, random, and often follow quiet periods.

How to fix

  1. Find the failing request in error.log and open the backend logs at the same second.
  2. If the process was killed, fix the memory use or the limit before touching nginx.
  3. If the app server timed out, make the endpoint faster or move the work to a background job. If it is legitimately slow, raise the app server timeout and keep it below nginx's proxy_read_timeout (60 seconds by default).
  4. For the keep-alive race, make the backend keep idle connections open longer than nginx does (nginx's upstream keepalive_timeout is 60 seconds), for example server.keepAliveTimeout = 65000 in Node.js.
  5. Test and reload: sudo nginx -t && sudo systemctl reload nginx, then replay the request with curl -i.

Is it just you?

If users report 502s but your logs look quiet, check the URL from outside your network with Is my site down?, which tests it from several regions at once.

Get alerted next time

  • An HTTP monitor fails as soon as nginx answers 502 instead of the status code you expect (2xx by default, or the exact code you set).
  • Each failure is rechecked from your other regions before an outage opens, so one dropped connection does not page anyone.
  • A TCP port monitor on the backend port, when it is reachable from outside, tells you whether the app is still listening behind nginx.