sfw/fix
(104: Connection reset by peer) high

nginx 502: recv() failed (104: Connection reset by peer) while reading response header from upstream

PHP-FPM (or the app upstream) reset the socket mid-response, so nginx had no headers to read and returned 502.

What you see

2026/06/15 09:41:03 [error] 31822#31822: *5567 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 203.0.113.9, server: example.com, request: "GET /checkout HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "example.com"

What’s actually happening

Visitors get a 502 Bad Gateway, and this line lands in /var/log/nginx/error.log. The key phrase is "reading response header from upstream" — nginx connected to FPM fine and sent the request, then FPM slammed the connection shut (errno 104, ECONNRESET) before sending back a complete header. This is the upstream's fault, not nginx's. It usually clusters: a burst at one timestamp, or every Nth request on a busy site. The matching FPM log almost always has a child-process death recorded at the same second.

Common causes

  • A PHP fatal error or uncaught exception that kills the worker before it flushes headers — check the FPM pool's php_admin_value[error_log] and the app log at the same timestamp.
  • memory_limit hit inside the request, killing the child mid-output (you'll see the "Allowed memory size exhausted" fatal, then the reset).
  • request_terminate_timeout in the FPM pool firing and SIGKILLing a long request, or an external OOM kill of the FPM child.
  • pm.max_requests recycling a worker exactly while it held a keepalive connection from nginx — the classic pm.max_requests vs nginx keepalive_requests / fastcgi_keep_conn race.
  • A segfaulting PHP extension (opcache, an old PECL build, a mismatched extension and PHP minor version) crashing the child with SIGSEGV.

How to fix it

  1. Open the FPM log at the exact error timestampMatch the nginx error second against /var/log/php8.2-fpm.log (path per your version). Look for "child %d exited on signal" or "child %d exited with code". Signal 11 = segfault, signal 9 = OOM/terminate-timeout kill, a normal exit code with a PHP fatal logged just before = application crash. That line tells you which of the causes below you actually have.
  2. Fix the application fatal first if there is oneIf the app log shows a fatal (memory exhausted, uncaught exception, a bad DB call), fix that — it's the real bug and raising limits just hides it. Turn on log_errors = On and display_errors = Off in the pool, and tail the app's own error log while you reproduce the failing request (e.g. /checkout above).
  3. Resolve the keepalive recycle raceIf FPM is recycling workers via pm.max_requests while nginx reuses upstream connections, you get sporadic resets with no app error. Either set pm.max_requests = 0 (no recycling) if you don't have a leak forcing it, or stop nginx from keepalive-ing to FPM: over fastcgi, remove fastcgi_keep_conn on; or in the upstream block drop the keepalive directive so each request gets a fresh connection. Keeping max_requests for leak control plus disabling upstream keepalive is the safe combination.
  4. Lift the timeout/memory ceilings if the work is legitimateFor genuinely long requests, raise request_terminate_timeout in the pool and fastcgi_read_timeout in nginx together (the smaller one wins). For memory, raise the pool's php_admin_value[memory_limit] and confirm total pm.max_children × per-worker memory still fits RAM, or you'll trade 502s for OOM kills.
  5. Chase a segfault by disabling extensionsIf the FPM log shows signal 11 with no PHP fatal, it's a C-level crash. Set opcache.enable=0 and retest; if that's not it, disable PECL extensions one at a time. Confirm every loaded .so matches your exact PHP minor version — a module built for 8.1 loaded under 8.2 segfaults intermittently.

Stop it recurring

Keep pm.max_children × per-worker memory under available RAM, and align FPM's request_terminate_timeout with nginx's fastcgi_read_timeout so neither layer kills a request the other still expects.

Related errors