Nginx 504: "Upstream Timed Out (110: Connection Timed Out)"

The app behind nginx didn't answer in time. First decide whether the app is legitimately slow (raise proxy_read_timeout) or hanging (fix the app). Blindly raising timeouts just moves the failure later.

What you'll see

Root causes

Legitimately slow upstream work

Long reports, batch endpoints, AI calls — jobs exceeding the default 60s proxy_read_timeout. The app finishes (logs prove it), nginx already gave up at exactly the timeout boundary.

Hanging upstream (deadlocks, exhausted pool, swallowed errors)

App never responds at all: DB lock, thread-pool exhaustion, or a handler waiting forever. Raising the timeout changes nothing — the app's own logs show where it stalls.

Fix it

  1. Confirm which variant: does the app eventually finish?
    # app logs: response completed AFTER nginx 504'd -> slow-but-working; nothing ever completes -> hang
  2. Slow-but-legitimate: raise the timeout for that path only
    # location /reports/ { proxy_read_timeout 300s; }   # scoped, not the whole server block
  3. Hanging: find the stall in the app
    # thread dump / py-spy / slow-query log — the 504 is a symptom; the app owns the bug
  4. Client-facing long jobs: switch to async
    # return 202 + job id + polling endpoint — timeouts disappear as a class for work >60s

Field note

proxy_read_timeout vs proxy_connect_timeout vs proxy_send_timeout are three different clocks: 110 while reading response header is read-timeout; connect-timeout failures log a different errno. Exact 60-second boundaries in failure timing = default timeout, always. It's the fastest fingerprint in this whole class.

Common questions

Is 504 always nginx's fault?

No — nginx is the messenger. The upstream failed to answer in the window. nginx fixes (timeouts) apply only to the slow-but-working variant; hangs need app-side debugging.

What's a safe proxy_read_timeout?

Match it to the real endpoint: normal APIs 60s is generous; long-job endpoints get their own scoped value (2-5min). Global 300s values just keep piles of hung workers alive — scope tight.

Ship it right the first time

Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.

Browse the template store →

One-time. Yours to modify. Instant download from the NinjaOps template store.