sfw/fix
110: Connection timed out high

nginx 504: upstream timed out (110: Connection timed out) while reading response header from upstream

nginx waited longer than its read timeout for your backend to send response headers and returned a 504.

What you see

upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.7, server: example.com, request: "GET /report HTTP/1.1", upstream: "fastcgi://unix:/run/php/php-fpm.sock"

What’s actually happening

This line is in nginx's error.log and the visitor got a 504 Gateway Timeout. nginx opened the connection to your backend fine — PHP-FPM, a Node app, whatever sits behind fastcgi_pass or proxy_pass — but the backend never sent response headers within proxy_read_timeout (or fastcgi_read_timeout), which defaults to 60 seconds. nginx gave up. The request wasn't rejected; it was too slow. You'll see it on heavy report pages, an unindexed query, or anything that calls a slow third-party API inside the request.

Common causes

  • A slow SQL query — missing index, full table scan, or lock contention — taking longer than the read timeout
  • A long-running PHP/Node operation (export, image processing, bulk import) running synchronously inside the request
  • An outbound API call with no timeout that hangs while nginx counts down
  • The backend is saturated — every PHP-FPM worker or Node event-loop is busy, so the request waits in a queue past the timeout
  • fastcgi_read_timeout / proxy_read_timeout set lower than how long the work legitimately takes

How to fix it

  1. Find the slow request and the slow queryGrep the nginx access log for the request line in the 504, note its $request_time. Then turn on the MySQL slow query log (or APM) and find the query behind that URL. Nine times out of ten it's one query missing an index — add it and the 504 disappears.
  2. Move long work out of the requestExports, emails, report generation, and third-party calls don't belong in a page load. Push them to a queue or cron worker and return immediately, polling or emailing the result. This is the real fix, not a timeout bump.
  3. Set timeouts on outbound callsAn external API with no client timeout can hang for minutes. Set a hard timeout (a few seconds) in the HTTP client so a slow dependency fails fast instead of dragging the whole request past nginx's limit.
  4. Raise the read timeout as a stopgapIn the location block add fastcgi_read_timeout 120s; (or proxy_read_timeout 120s; for proxy_pass), and lift PHP-FPM's request_terminate_timeout to match. Reload nginx. This stops the bleeding while you fix the slow path — it doesn't fix it.
  5. Check backend saturationIf pm.max_children is too low for your traffic, requests queue behind busy workers and time out. Compare active processes to the limit and raise it if the box has the RAM.

Stop it recurring

Alert on nginx $request_time and the MySQL slow query log so a query creeping toward the timeout gets fixed before it 504s.

Related errors