Envoy Gateway 504 Upstream Request Timeout After 15 Seconds

Tested on RHEL 10.2 (Coughlan)
Package kubectl v1.37.0
Envoy Gateway v1.9.2
Gateway 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
Short on time?Beta Get the gist of this 6-minute read in seconds, then ask follow-up questions

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:

bash
curl -sS -D - --max-time 25 -o slept.txt -H 'Host: app.example.com' \
  'http://127.0.0.1:19080/?seconds=3'
output
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: 10

A 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:

bash
curl -sS -D - --max-time 30 -o slept.txt -H 'Host: app.example.com' \
  'http://127.0.0.1:19080/?seconds=16'
output
HTTP/1.1 504 Gateway Timeout
content-length: 24
content-type: text/plain
date: Sat, 03 Oct 2026 19:18:28 GMT

The 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:

  • 60s is sixty seconds. A bare 60 is not valid here.
  • 0s disables this request timeout. Use that only when the call is allowed to run without this deadline. This page did not apply 0s.

60s below is a stand-in. Use the longest call your clients should wait for:

yaml
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: 80

A shorter check used request: 4s and backendRequest: 4s:

  • A 3 second sleep returned HTTP 200 and slept=3.0 at 19:19:13
  • A 5 second sleep returned HTTP 504 and upstream request timeout at 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:

  • request is the whole client call
  • backendRequest is one call from the gateway to a backend
  • backendRequest cannot be longer than request. The API rejects that HTTPRoute.
yaml
timeouts:
  request: 10s
  backendRequest: 2s
Client request budget     10s
Backend attempt budget     2s
                           deadline for that backend call

With 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:

  • backendRequest when one upstream try must be shorter than the overall client deadline
  • request when 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.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)