504 Gateway Timeout: what it means and how to fix it
Kofi Agyem, Founder and CEO, CloudzyroPublished · 5 min read
On this page
A 504 Gateway Timeout means a proxy waited too long for the server behind it. Why it happens, timeouts in nginx, Apache and Cloudflare, and how to fix it.
1. The short answer
A 504 Gateway Timeout means a server in the middle, such as a proxy, load balancer or CDN, waited for the server behind it and gave up before an answer came. The site's app is too slow, stuck, or unreachable. It isn't your connection: the timeout happens between the website's own servers.
It's a close cousin of 502 Bad Gateway. A 502 means the proxy got a bad answer or none; a 504 means it ran out of time waiting.
2. What you'll see
"504 Gateway Time-out" from nginx, "Gateway Timeout" from Apache or a cloud load balancer, or a Cloudflare page. Cloudflare usually reports a slow server as 524 A timeout occurred, and a server it can't connect to as 522 Connection timed out. Both are Cloudflare's own codes, not standard HTTP ones.
3. Common causes
- Slow work inside the request. A big report, an import, image processing or a long AI call that runs while the visitor waits.
- A slow database query, often a missing index on a table that has grown.
- Waiting on another API that's slow or down, with no timeout of its own.
- The server is overloaded, so requests queue until the proxy gives up.
- PHP-FPM has no free workers, because every worker is busy with a slow request.
- A firewall drops packets between the proxy and the app, so the connection hangs instead of failing fast.
- Wrong DNS for the backend, pointing the proxy at an address that never answers.
4. How to fix a 504 as a visitor
- Wait a minute and refresh.
- If you'd started something big, such as an export or an upload, check whether it finished before you try again.
- Check the site's status page for an outage.
- If it lasts, tell the site owner which page and when.
5. How to fix a 504 on your own site
Find the slow part first. The proxy's log says it timed out; the app's log, the database's slow query log, or timing around each step says why.
nginx. The error log says upstream timed out (110: Connection timed out) while reading response header from upstream. proxy_read_timeout (for proxied apps) and fastcgi_read_timeout (for PHP-FPM) both default to 60 seconds. Raise them only for the routes that need it.
# nginx: allow one slow route longer, keep the rest at 60s
location /reports/ {
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 120s;
}Apache. ProxyTimeout sets how long mod_proxy waits; it defaults to the server's Timeout, which is 60 seconds. Look for The timeout specified has expired in the error log.
PHP and WordPress. max_execution_time (30 seconds by default) stops long scripts, which usually shows as a 500; a 504 means the proxy's own limit came first. Look for slow plugins, scheduled tasks running on page loads, and calls to outside services. Page caching helps a lot on busy WordPress sites.
Node, Express and Next.js. Put a timeout on every outside call (fetch with AbortSignal.timeout(10000)), add database indexes, and move long jobs to a queue or background worker. Answer the request with "we're on it", then show the result when it's ready.
Cloudflare. Cloudflare waits 125 seconds by default for your server to answer, then shows 524. Below Enterprise you can't raise that, so the fix is to make the request faster or move the work to the background.
6. On Cloudzyro
Cloudflare sits in front of every Sites & Apps site, so a page that takes more than 125 seconds gets Cloudflare's timeout error. Run slow work such as big imports, reports or long AI calls in the background, answer the request straight away, and show the result when it's ready. The domain troubleshooting guide covers this too. Builds have their own, separate 15-minute limit, explained in the failed deploy guide.
7. A worked example
A team's dashboard has an "Export all orders" button. It worked for months, then started showing 504.
- Timing the request with
curl -o /dev/null -s -w "%{http_code} %{time_total}\n"prints504 60.01: exactly nginx's 60-second default. - The app's log shows the export query now takes 90 seconds; the orders table has grown tenfold.
- They add an index on the column the export filters by, which brings the query down to a few seconds.
- For the future, they move the export to a background job that emails a download link, so it can never time out a request.
8. How to check the status code and timing
# Status and how long it took
curl -o /dev/null -s -w "%{http_code} %{time_total}s\n" https://www.example.com/slow-page
# Headers only
curl -I https://www.example.com/slow-pageA time that lands on a round number (30, 60 or 125 seconds) points straight at the timeout that fired. In browser developer tools, the Network tab shows the status, and the Timing view shows how long the browser waited for the first byte.
9. Related errors
- [502 Bad Gateway](/guides/what-is-a-502-error): a bad answer or a refused connection, rather than a slow one.
- [503 Service Unavailable](/guides/503-service-unavailable): the server says it can't take requests right now.
- [500 Internal Server Error](/guides/500-internal-server-error): the app ran and failed.
- 408 Request Timeout: the other way round; the server gave up waiting for the browser to finish sending.
If timeouts keep coming back, uptime monitoring tells you before your visitors do.