| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | kubectl v1.37.0Envoy Gateway v1.9.2Gateway API CRDs v1.6.1 (standard channel) |
| Applies to | Any host with kubectl configured; any Kubernetes cluster that already runs Envoy Gateway |
| Assumes | An existing HTTPRoute to an application that can delay its response |
| Privilege | Normal user |
| Scope | Why a normal HTTP response returns 504 at about 15 seconds, and how timeouts.request and timeouts.backendRequest set that deadline. Does not cover upload receive timeouts, keep-alive idle close, or WebSocket inactivity. |
| Related guides | Migrate proxy-read-timeout to Envoy Gateway Fix a slow Envoy Gateway upload WebSocket idle disconnects Migrate ingress-nginx to Envoy Gateway Gateway API with HTTPRoute |
Envoy Gateway v1.9.2 gives an HTTP request about 15 seconds when the HTTPRoute does not set a timeout:
- A backend that sleeps for 3 seconds still returns HTTP 200
- A backend that sleeps for 16 seconds returns HTTP 504 and the body
upstream request timeout
The proxy stops the wait. It does not wait for the app.
That 15 second value is timeouts.request, an absolute deadline for the client call. It is not the gap between upstream reads. If you are moving an ingress-nginx proxy-read-timeout, read that migration before you copy the number onto this field.
Why the request returns 504 at 15 seconds
A 3 second sleep fits the default clock. Send it and read the status. The application answered, and the body was slept=3.0:
curl -sS -D - --max-time 25 -o slept.txt -H 'Host: app.example.com' \
'http://127.0.0.1:19080/?seconds=3'HTTP/1.1 200 OK
server: BaseHTTP/0.6 Python/3.12.15
date: Sat, 03 Oct 2026 19:18:13 GMT
content-type: text/plain
content-length: 10A 16 second sleep does not fit. The 504 is timestamped 19:18:28, about 15 seconds after that 200, not when a 16 second sleep would have finished:
curl -sS -D - --max-time 30 -o slept.txt -H 'Host: app.example.com' \
'http://127.0.0.1:19080/?seconds=16'HTTP/1.1 504 Gateway Timeout
content-length: 24
content-type: text/plain
date: Sat, 03 Oct 2026 19:18:28 GMTThe body is upstream request timeout. In this test that plain-text line came from Envoy. It is not an nginx HTML 504 page.
These captures used a local address for the Gateway. Send the same Host and path to the hostname your Gateway publishes.
Set timeouts.request
Gateway API puts the deadline on the HTTPRoute rule. The values are durations:
60sis sixty seconds. A bare60is not valid here.0sdisables this request timeout. Use that only when the call is allowed to run without this deadline. This page did not apply0s.
60s below is a stand-in. Use the longest call your clients should wait for:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web
spec:
parentRefs:
- name: eg
hostnames:
- app.example.com
rules:
- timeouts:
request: 60s
backendRefs:
- name: web
port: 80A shorter check used request: 4s and backendRequest: 4s:
- A 3 second sleep returned HTTP 200 and
slept=3.0at 19:19:13 - A 5 second sleep returned HTTP 504 and
upstream request timeoutat 19:19:17, about 4 seconds later, not 15
Prefer these HTTPRoute fields for ordinary HTTP:
- A BackendTrafficPolicy request timeout is a different control. Use it when the route cannot express the timeout you need, such as some gRPC streams.
- Do not expect that policy to override an HTTPRoute that already sets
timeouts.request.
An upgraded WebSocket is not this clock. WebSocket disconnects are the idle stream, not timeouts.request.
Set timeouts.backendRequest
timeouts.request and timeouts.backendRequest are different budgets:
requestis the whole client callbackendRequestis one call from the gateway to a backendbackendRequestcannot be longer thanrequest. The API rejects that HTTPRoute.
timeouts:
request: 10s
backendRequest: 2sClient request budget 10s
Backend attempt budget 2s
deadline for that backend callWith those two values, a 3 second sleep returned HTTP 504 and upstream request timeout. The 10 second client budget did not keep the wait open. The 2 second backend clock ended it.
Use the shorter field only when you need it:
backendRequestwhen one upstream try must be shorter than the overall client deadlinerequestwhen you only need that overall deadline
Troubleshooting
| Symptom | Likely cause | What to change |
|---|---|---|
HTTP 504 and upstream request timeout at about 15 seconds |
timeouts.request is unset |
Set timeouts.request to the deadline you want, such as 60s |
HTTP 504 sooner than timeouts.request |
backendRequest is shorter and fires first |
Raise backendRequest, or remove it if the request deadline is the one you want |
| The API rejects the HTTPRoute | backendRequest is longer than request |
Make backendRequest less than or equal to request |
| A streamed response dies even though data is still moving | timeouts.request is an absolute deadline |
That is a different clock from an inactivity gap. See the proxy-read-timeout migration |
HTTP 408 and request timeout while the body is still uploading |
The receive clock, not this 504 | That is the slow-upload timeout |
| The client connection closes after a finished response | Keep-alive idle time | ClientTrafficPolicy timeout.http.idleTimeout is an inactive connection. It is not this request deadline |
Summary
With no HTTPRoute timeout, Envoy Gateway v1.9.2 returned HTTP 200 for a 3 second sleep and HTTP 504 for a 16 second sleep. The 504 body was upstream request timeout, and the wait stopped at about 15 seconds.
timeouts.request is that deadline. At 4 seconds, a 3 second sleep succeeded and a 5 second sleep returned 504. timeouts.backendRequest of 2 seconds, under a 10 second request budget, returned 504 for a 3 second sleep. backendRequest cannot be longer than request.
This is not proxy-read-timeout, and it is not the timer that closes an idle keep-alive connection after the response is done.
References
Frequently Asked Questions
Why does Envoy Gateway return 504 after about 15 seconds?
The HTTPRoute has no timeouts.request, so the v1.9.2 default of about 15 seconds applies. A 16 second sleep returned HTTP 504 and upstream request timeout. A 3 second sleep returned HTTP 200.
What is the difference between request and backendRequest?
request is the whole client call. backendRequest is one gateway-to-backend call and cannot be longer than request. With request: 10s and backendRequest: 2s, a 3 second sleep returned 504. The client budget did not extend the backend call.
Can I turn the request timeout off?
Gateway API accepts 0s on timeouts.request to disable that deadline. Do that only for a call that is allowed to run without it. This page did not apply 0s.
Is this the same as proxy-read-timeout?
No. proxy-read-timeout is a gap between upstream reads. timeouts.request ends the call when the duration is reached, even if you meant to allow a long active response.

