A 504 Gateway Timeout error means a server acting as a gateway or proxy waited for an answer from the server behind it and gave up. The request reached your CDN, load balancer or web server, was passed on to the application, and the application did not respond before the time limit ran out.
The server behind the proxy is often still running. It’s just too slow, or something it waits for is. That makes a 504 different from most outages: restarting things rarely fixes it for long. This guide shows how to find which timeout fired, what usually makes the request slow and how to fix the cause rather than the symptom.
What a 504 tells you
A request to a modern website passes through several layers, and each one has its own limit on how long it waits for the next:
- A CDN such as Cloudflare waits a fixed time for your origin.
- A load balancer has an idle or response timeout per target.
- nginx waits for the upstream according to
proxy_read_timeoutorfastcgi_read_timeout, 60 seconds by default. - The application has its own limits, like PHP’s
max_execution_timeor a worker timeout in Gunicorn.
The 504 comes from the first layer whose limit runs out. Its neighbours mean something else: a 502 Bad Gateway means the upstream answered with something invalid or not at all, and a 503 Service Unavailable means the service refused the request because it is overloaded or in maintenance.
Find the timeout that fired
Time the request. A 504 that always arrives after the same number of seconds points at one specific limit:
curl -s -o /dev/null -w "status %{http_code} after %{time_total}s\n" https://www.example.com/reports
status 504 after 60.04s
Then look at the response headers and the error page to see which layer answered, as with any 5xx error. A plain nginx page with server: nginx means your own web server timed out. A CDN-branded page that names your host means the CDN timed out waiting for your origin.
nginx logs the timeout with the address it was waiting for:
upstream timed out (110: Connection timed out) while reading response header from upstream,
upstream: "fastcgi://unix:/run/php/php8.3-fpm.sock", request: "GET /reports HTTP/2.0"
Now you know which page is slow and which part of the stack kept it waiting. The next step is finding out why.
Common causes and how to fix them
Slow database queries
The most common reason. A query that took 50 milliseconds with a thousand rows takes 40 seconds with a million, because it scans the whole table. Turn on the slow query log and look for the page’s queries:
# MySQL / MariaDB
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
# PostgreSQL, in postgresql.conf
log_min_duration_statement = 1000
Run the slowest query with EXPLAIN and look for full table scans. A missing index is the usual fix. Locks are the other usual suspect: a long transaction or a migration can hold a table while every request that needs it waits.
Slow external APIs
Pages often call other services while they’re being built: a payment provider, a shipping calculator, a search engine, a CRM. If that service is slow, your page is slow, and your proxy times out. Set a short timeout on every outgoing call, a few seconds rather than the library default, and decide what the page shows when the call fails. A shipping estimate that says “calculated at checkout” is better than a 504.
Keep an eye on the services you depend on. Uptimecheck’s third-party monitoring follows the status pages of 31 services such as Klarna, Shopify and Cloudflare, so a slow provider shows up next to your own checks.
Too few workers for the traffic
When every worker is busy, new requests wait in a queue. They aren’t slow themselves, they are waiting their turn, and the proxy’s clock is running the whole time. PHP-FPM says so in its log with server reached pm.max_children. More workers help only if the server has memory and CPU for them. Often the better fix is making each request shorter, so the same workers serve more of them, and caching pages that don’t change per visitor.
Long jobs inside a web request
Exports, imports, report generation, image processing and sending hundreds of emails don’t belong in a web request. They take as long as they take, and sooner or later one of them crosses a timeout. Move them to a background queue: the request starts the job and returns at once, and the page polls for the result or the user gets an email when it’s done.
The server itself is overloaded
High CPU, memory pressure that pushes the server into swap, or a disk that can’t keep up make everything slow at once. A 504 on every page, not just one, points here. Look at CPU, memory, swap and disk wait for the same period:
uptime
free -m
iostat -x 5 3
Server monitoring keeps that history, so you can see what the server was doing at the minute the 504s started instead of guessing afterwards.
Network problems between the layers
If the proxy can’t reach the upstream at all, for example because a firewall drops packets instead of rejecting them, the connection hangs until it times out. That ends in a 504 rather than a 502. Check that the proxy can open a connection to the upstream’s address and port from the same machine.
When raising the timeout is right
Sometimes a request is legitimately long: a large upload, a report an admin asks for once a month. Then raise the limit for that path only, and keep the layers in order so the outer ones wait longer than the inner ones:
location /admin/exports/ {
proxy_read_timeout 300s;
proxy_pass http://app;
}
Raising the limit for the whole site doesn’t fix slow pages. It makes visitors wait longer before they see an error, and it lets slow requests hold workers for longer, which makes the queue worse for everyone else.
How monitoring catches 504s
A 504 rarely comes out of nowhere. The page got a little slower every week as the data grew, until one day it crossed the limit. That’s good news, because it means you can see it coming.
- Watch the response time, not only the status. Uptimecheck records the response time of every check, split into DNS, connect, TLS, server and download, so you see whether the server got slower or the network did. A slow-response alert tells you when a page crosses the limit you set, long before the proxy’s timeout. Our guide to what a good response time is helps you pick the limit.
- Confirm failures before alerting. Checks run every minute from Stockholm, Sweden, and every failure is confirmed by a second check, so one slow request doesn’t wake anyone up.
- Monitor the slow pages directly. Search, checkout, account pages and reports are where 504s happen, and they are rarely the homepage. Add uptime monitors for them, with a timeout that matches what a visitor will put up with.
504 checklist
- Time the request: a round number of seconds tells you which limit fired.
- Read the headers and error page to see which layer timed out.
- Find the slow page in the proxy’s error log.
- Look for slow queries, locks and slow external calls for that page.
- Check workers, CPU, memory, swap and disk for the same minutes.
- Fix the slow part, cache it or move it to a background job.
- Raise timeouts only for paths that are legitimately long, outer layers longer than inner ones.
- Alert on slow responses so the next 504 is seen while it’s still a slowdown.