HTTP 504 Gateway Timeout
A 504 comes from a proxy, load balancer or CDN that forwarded the request and stopped waiting before an answer came back. The upstream may still be working on the request, and may even finish it, but the client already has an error.
504 Gateway Timeout
A gateway or proxy didn't receive a timely response from the upstream server.
Who fixes it: Server. The upstream is too slow for the proxy's timeout. Fix the slowness, or raise the timeouts at every layer.
What 504 means
That matters for non-idempotent requests: a POST /payments that got a 504 may have succeeded. Clients should check before retrying, or use idempotency keys.
The fix is either to make the upstream faster or to raise the timeout at every layer between client and app. Raising timeouts alone usually just moves the problem.
Source: RFC 9110 §15.6.5.
Common causes
- nginx
proxy_read_timeout(default 60s) expired. The error log showsupstream timed out (110: Connection timed out) while reading response header from upstream. - A slow database query (missing index, lock wait) or an external API call with no timeout of its own.
- A load balancer's idle timeout is shorter than the request. AWS ALB defaults to 60 seconds. Cloudflare waits 100 seconds for the origin by default and reports a timeout with its own 524 code, not 504.
- Vercel: the function exceeded its maximum duration, reported as
504: FUNCTION_INVOCATION_TIMEOUT. - PHP-FPM behind nginx:
fastcgi_read_timeout(60s by default) or PHP'smax_execution_time. - The upstream is unreachable at the network level (a firewall silently dropping packets, a wrong security group), so the connection attempt times out after
proxy_connect_timeout. - Worker starvation: all workers are busy, so new requests queue until the proxy gives up.
How to debug and fix a 504
Measure where the time goes
Time to first byte through the proxy vs directly from the app tells you whether the app is slow or the path to it is.
curl -s -o /dev/null \
-w 'connect: %{time_connect}s ttfb: %{time_starttransfer}s total: %{time_total}s\n' \
https://example.com/api/report
# Bypass the proxy
curl -s -o /dev/null -w '%{time_total}s\n' http://127.0.0.1:3000/api/reportnginx: raise timeouts only where needed
Keep the connect timeout short so an unreachable app fails fast, and raise the read timeout only for the slow endpoints.
location /api/reports/ {
proxy_pass http://127.0.0.1:3000;
proxy_connect_timeout 5s; # fail fast if the app is unreachable
proxy_send_timeout 120s;
proxy_read_timeout 120s; # default 60s
}
# PHP-FPM equivalent
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_read_timeout 120s;
}Next.js on Vercel: maxDuration
Set the route segment config in the route or page. The maximum allowed value depends on your plan.
// app/api/report/route.ts
export const maxDuration = 60; // secondsMove long work out of the request
If a job takes minutes, no timeout setting is the right fix. Accept it with 202, process it in a queue, and let the client poll.
POST /api/reports -> 202 Accepted
Location: /api/reports/jobs/91
GET /api/reports/jobs/91 -> 200 {"status": "running"}
GET /api/reports/jobs/91 -> 200 {"status": "done", "url": "/files/report-91.csv"}Headers that matter with 504
- Server-Timing
- Have the app report where time goes (
Server-Timing: db;dur=850, render;dur=120). Browsers show it in DevTools' Timing tab. - Retry-After
- Optional. Clients should retry idempotent requests with backoff.
504 vs similar status codes
- 502 Bad Gateway: The upstream responded badly or not at all (refused, reset). 504 means it was simply too slow.
- 503 Service Unavailable: The service declined the request up front, e.g. maintenance or overload.
- 408 Request Timeout: The reverse case: the server timed out waiting for the client to send its request.
SEO impact of 504
Timeouts count as server errors for Googlebot: it slows crawling, and pages that time out repeatedly can drop out of the index. Slow responses also reduce how much of a large site gets crawled, so fixing the slow query does more than raising the timeout.
Related status codes
502Bad Gateway
A gateway or proxy received an invalid response from the upstream server it contacted.
503Service Unavailable
The server is temporarily unable to handle the request, due to overload or scheduled maintenance.
500Internal Server Error
The server encountered an unexpected condition that prevented it from fulfilling the request.
413Content Too Large
The request content is larger than the server is willing or able to process.
Looking for another code? Search the full HTTP status codes list, or read which code your API should return in the REST API status codes guide.
Learn more
HTTP Status Codes for REST APIs: 200 vs 201 vs 204, 400 vs 422 vs 429
A practical guide to choosing the right HTTP status code in REST API design. Covers the most confusing pairs: 200 vs 201, 400 vs 422, 401 vs 403, and when to use 503 vs 429.
REST API Design Best Practices: A Practical Guide
The conventions that make REST APIs intuitive and consistent. Covers URL design, HTTP methods, status codes, versioning, pagination, and error responses.
CORS Explained: Why It Happens and How to Fix It
CORS errors happen when a browser blocks a request to a different origin. Learn how the Same-Origin Policy works, what preflight requests are, and how to configure CORS correctly in Express, FastAPI, and Nginx.
Frequently Asked Questions
What causes a 504 Gateway Timeout?▾
A proxy, load balancer or CDN waited longer than its timeout for the backend. The usual root causes are slow database queries, slow third-party API calls, overloaded workers, or a network path to the backend that silently drops packets.
How do I increase the timeout in nginx?▾
Set proxy_read_timeout (and proxy_send_timeout) in the location that proxies to your app, for example proxy_read_timeout 120s;, then reload nginx. For PHP-FPM use fastcgi_read_timeout. Raise the load balancer and CDN timeouts too, since the shortest one wins.
Is it safe to retry a request that got a 504?▾
For GET and other idempotent requests, yes, with backoff. For POST requests the server may have finished the work after the proxy gave up, so a retry can duplicate it. Use idempotency keys or check the resource's state first.