envoyproxy / envoyproxy/envoy

HTTP/3 proxying fails with 503 and upstream reset

Open
#40,994 11 comments 0 reactions 0 assignees View on GitHub
area/quic bug no stalebot
Dominant language
C++
Stars
28.9k
Forks
5.6k
Avg merge
1d 20h
Merged PRs (30d)
437

Description

**Description:**
I am setting up an Envoy proxy to terminate an HTTP/3 connection from a client and forward it as an HTTP/3 connection to an upstream service. While a direct HTTP/3 request from the client to the upstream service works correctly (returns 200), the request through the Envoy proxy fails with a 503 error.

The Envoy logs indicate the upstream connection was reset by the peer before the response started: `upstream_reset_before_response_started{connection_termination|QUIC_IETF_GQUIC_ERROR_MISSING|FROM_PEER|}.`

**Expected behavior:** The request proxied through Envoy should return a 200 status code, just like the direct request.

**Repro steps:**

Environment: Kubernetes cluster.

Services:

http3-server: An HTTP/3 server running in Kubernetes (port 9000/UDP).

envoy-proxy: Envoy deployed with the provided configuration, listening on port 8091/UDP (for QUIC) and 8091/TCP.

Test Client: A pod running the alpine/curl-http3:8.11.0 image in the same namespace.

Direct Request (works):

```
curl -I -v --http3-only https://http3-server.ns.cluster.local:9000/stream \
--cacert /vault/secrets/ca_cert \
--cert /vault/secrets/server_cert \
--key /vault/secrets/server_key
* Host http3-server.ns.cluster.local:9000 was resolved.
* IPv6: (none)
* IPv4: 10.10.10.10
* Trying 10.10.10.10:9000...
* CAfile: /vault/secrets/ca_cert
* CApath: /etc/ssl/certs
* Server certificate:
* subject: CN=ns
* start date: Jul 15 09:29:37 2025 GMT
* expire date: Oct 3 09:30:06 2027 GMT
* subjectAltName: host "http3-server.ns.cluster.local" matched cert's "*.ns.cluster.local"
* issuer: CN=CA
* SSL certificate verify ok.
* Connected to http3-server.ns.cluster.local (10.10.10.10) port 9000
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://http3-server.ns.cluster.local:9000/stream
* [HTTP/3] [0] [:method: HEAD]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: http3-server.ns.cluster.local]
* [HTTP/3] [0] [:path: /stream]
* [HTTP/3] [0] [user-agent: curl/8.11.0-DEV]
* [HTTP/3] [0] [accept: */*]
> HEAD /stream HTTP/3
> Host: http3-server.ns.cluster.local
> User-Agent: curl/8.11.0-DEV
> Accept: */*
>
* Request completely sent off
< HTTP/3 200
HTTP/3 200
< date: Fri, 05 Sep 2025 06:24:23 GMT
date: Fri, 05 Sep 2025 06:24:23 GMT
```

Proxied Request (fails):

```
curl -I -v --http3-only https://envoy-proxy.ns.cluster.local:8091/stream \
--cacert /vault/secrets/ca_cert \
--cert /vault/secrets/server_cert \
--key /vault/secrets/server_key
* Host envoy-proxy.ns.cluster.local:8091 was resolved.
* IPv6: (none)
* IPv4: 10.10.10.11
* Trying 10.10.10.11:8091...
* CAfile: /vault/secrets/ca_cert
* CApath: /etc/ssl/certs
* Server certificate:
* subject: CN=ns
* start date: Sep 1 12:22:07 2025 GMT
* expire date: Nov 20 12:22:35 2027 GMT
* subjectAltName: host "envoy-proxy.ns.cluster.local" matched cert's "*.ns.cluster.local"
* issuer: CN=CA
* SSL certificate verify ok.
* Connected to envoy-proxy.ns.cluster.local (10.10.10.11) port 8091
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://envoy-proxy.ns.cluster.local:8091/stream
* [HTTP/3] [0] [:method: HEAD]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: envoy-proxy.ns.cluster.local]
* [HTTP/3] [0] [:path: /stream]
* [HTTP/3] [0] [user-agent: curl/8.11.0-DEV]
* [HTTP/3] [0] [accept: */*]
> HEAD /stream HTTP/3
> Host: envoy-proxy.ns.cluster.local
> User-Agent: curl/8.11.0-DEV
> Accept: */*
>
* Request completely sent off
< HTTP/3 503
HTTP/3 503
< content-length: 163
content-length: 163
< content-type: text/plain
content-type: text/plain
< date: Fri, 05 Sep 2025 06:54:19 GMT
date: Fri, 05 Sep 2025 06:54:19 GMT
< server: envoy
server: envoy
```

**Envoy Config:**
The Envoy configuration YAML used is as follows:

```
static_resources:
listeners:
- name: quic_listener # HTTP/3
address:
socket_address:
address: 0.0.0.0
port_value: 8091
protocol: UDP
udp_listener_config:
quic_options: {}
filter_chains:
- transport_socket:
name: envoy.transport_sockets.quic
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.quic.v3.QuicDownstreamTransport
downstream_tls_context:
common_tls_context:
validation_context:
trusted_ca:
filename: "/etc/envoy/cert/ca.crt"
trust_chain_verification: ACCEPT_UNTRUSTED
tls_certificates:
- certificate_chain:
filename: "/etc/envoy/cert/listener0.crt"
private_key:
filename: "/etc/envoy/cert/listener0.key"
alpn_protocols: ["h3"]
filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
codec_type: HTTP3
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: http_http_auth_service
http3_protocol_options:
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
- name: listener_http_auth
address:
socket_address:
address: 0.0.0.0
port_value: 8091
protocol: TCP
per_connection_buffer_limit_bytes: 32768
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
codec_type: HTTP2
stat_prefix: ingress_http
generate_request_id: true
access_log:
- name: envoy.access_loggers.file
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
path: "/dev/stdout"
typed_json_format:
timestamp: "%START_TIME%"
authority: "%REQ(:AUTHORITY)%"
bytes_received: "%BYTES_RECEIVED%"
bytes_sent: "%BYTES_SENT%"
downstream_local_address: "%DOWNSTREAM_LOCAL_ADDRESS%"
downstream_remote_address: "%DOWNSTREAM_REMOTE_ADDRESS%"
duration: "%DURATION%"
method: "%REQ(:METHOD)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
protocol: "%PROTOCOL%"
request_id: "%REQ(X-REQUEST-ID)%"
requested_server_name: "%REQUESTED_SERVER_NAME%"
response_code: "%RESPONSE_CODE%"
response_flags: "%RESPONSE_FLAGS%"
uber_trace_id: "%REQ(UBER-TRACE-ID)%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
upstream_host: "%UPSTREAM_HOST%"
upstream_local_address: "%UPSTREAM_LOCAL_ADDRESS%"
upstream_service_time: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"
user_agent: "%REQ(USER-AGENT)%"
x_forwarded_for: "%REQ(X-FORWARDED-FOR)%"
client_ip: "%REQ(X-ENVOY-EXTERNAL-ADDRESS)%"
upgrade_configs:
- upgrade_type: websocket
use_remote_address: true
common_http_protocol_options:
idle_timeout: 3600s # 1 hour
headers_with_underscores_action: REJECT_REQUEST
http2_protocol_options:
max_concurrent_streams: 100
initial_stream_window_size: 65536
initial_connection_window_size: 1048576
stream_idle_timeout: 300s
request_timeout: 300s
route_config:
name: local_route
virtual_hosts:
- name: local_service
response_headers_to_add:
- header:
key: alt-svc
value: h3=":10000"; ma=86400, h3-29=":10000"; ma=86400
domains: "*"
cors:
allow_origin_string_match:
- safe_regex:
google_re2: { }
regex: \*
allow_methods: "GET, POST, OPTIONS, PATCH"
filter_enabled:
default_value:
numerator: 100
denominator: HUNDRED
runtime_key: cors.www.enabled
shadow_enabled:
default_value:
numerator: 0
denominator: HUNDRED
runtime_key: cors.www.shadow_enabled
routes:
- match: { prefix: "/" }
route:
cluster: http_http_auth_service
idle_timeout: 15s
timeout: 60s
http3_protocol_options:
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
require_client_certificate: true
common_tls_context:
validation_context:
trusted_ca:
filename: "/etc/envoy/cert/ca.crt"
trust_chain_verification: ACCEPT_UNTRUSTED
tls_certificates:
- certificate_chain:
filename: "/etc/envoy/cert/listener0.crt"
private_key:
filename: "/etc/envoy/cert/listener0.key"
clusters:
- name: admin_local
connect_timeout: 0.1s
upstream_connection_options:
tcp_keepalive:
keepalive_interval: 60
keepalive_time: 600
type: STRICT_DNS
lb_policy: ROUND_ROBIN
dns_lookup_family: V4_ONLY
http2_protocol_options: {}
load_assignment:
cluster_name: admin_local
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.2
port_value: 9902
- name: http_http_auth_service
connect_timeout: 30s
per_connection_buffer_limit_bytes: 32768
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: http_http_auth_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: http3-server.ns.cluster.local
port_value: 9000
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http3_protocol_options: {}
common_http_protocol_options:
idle_timeout: 1s
transport_socket:
name: envoy.transport_sockets.quic
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.quic.v3.QuicUpstreamTransport
upstream_tls_context:
sni: http3-server.ns.cluster.local
common_tls_context:
validation_context:
trusted_ca:
filename: "/etc/envoy/cert/ca.crt"
trust_chain_verification: ACCEPT_UNTRUSTED
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 127.0.0.2
port_value: 9902
```

**Logs:**
Relevant Envoy log output for the failed request:

```
[2025-09-05 06:54:19.777][14][debug][http] [source/common/http/conn_manager_impl.cc:393] [Tags: "ConnectionId":"10743666436109182153"] new stream
[2025-09-05 06:54:19.777][14][debug][quic_stream] [source/common/quic/envoy_quic_server_stream.cc:169] [Tags: "ConnectionId":"10743666436109182153","StreamId":"0"] Received headers: { :method=HEAD, :scheme=https, :authority=envoy-proxy.ns.cluster.local, :path=/stream, user-agent=curl/8.11.0-DEV, accept=*/*, }.
[2025-09-05 06:54:19.777][14][debug][http] [source/common/http/conn_manager_impl.cc:1183] [Tags: "ConnectionId":"10743666436109182153","StreamId":"5702234508651400730"] request headers complete (end_stream=false):
':method', 'HEAD'
':scheme', 'https'
':authority', 'envoy-proxy.ns.cluster.local'
':path', '/stream'
'user-agent', 'curl/8.11.0-DEV'
'accept', '*/*'

[2025-09-05 06:54:19.777][14][debug][quic_stream] [source/common/quic/envoy_quic_server_stream.cc:254] [Tags: "ConnectionId":"10743666436109182153","StreamId":"0"] Received 0 bytes of data with FIN.
[2025-09-05 06:54:19.777][14][debug][http] [source/common/http/conn_manager_impl.cc:1166] [Tags: "ConnectionId":"10743666436109182153","StreamId":"5702234508651400730"] request end stream timestamp recorded
[2025-09-05 06:54:19.887][14][debug][quic_stream] [source/common/quic/envoy_quic_client_stream.cc:40] [Tags: "ConnectionId":"7828035694118677611","StreamId":"0"] encodeHeaders: (end_stream=false) ':method', 'HEAD'
':scheme', 'https'
':authority', 'envoy-proxy.ns.cluster.local'
':path', '/stream'
'user-agent', 'curl/8.11.0-DEV'
'accept', '*/*'
'x-forwarded-proto', 'https'
'x-request-id', '0e1725a5-08f1-4a56-b458-7881ad1b8362'
'x-envoy-expected-rq-timeout-ms', '15000'
.
[2025-09-05 06:54:19.887][14][debug][quic_stream] [source/common/quic/envoy_quic_stream.cc:14] [Tags: "ConnectionId":"7828035694118677611","StreamId":"0"] encodeData (end_stream=true) of 0 bytes.
[2025-09-05 06:54:19.888][14][debug][http] [source/common/http/filter_manager.cc:1084] [Tags: "ConnectionId":"10743666436109182153","StreamId":"5702234508651400730"] Sending local reply with details upstream_reset_before_response_started{connection_termination|QUIC_IETF_GQUIC_ERROR_MISSING|FROM_PEER|}
[2025-09-05 06:54:19.888][14][debug][http] [source/common/http/conn_manager_impl.cc:1878] [Tags: "ConnectionId":"10743666436109182153","StreamId":"5702234508651400730"] encoding headers via codec (end_stream=true):
':status', '503'
'content-length', '163'
'content-type', 'text/plain'
'date', 'Fri, 05 Sep 2025 06:54:19 GMT'
'server', 'envoy'

[2025-09-05 06:54:19.888][14][debug][quic_stream] [source/common/quic/envoy_quic_server_stream.cc:59] [Tags: "ConnectionId":"10743666436109182153","StreamId":"0"] encodeHeaders (end_stream=true) ':status', '503'
'content-length', '163'
'content-type', 'text/plain'
'date', 'Fri, 05 Sep 2025 06:54:19 GMT'
'server', 'envoy'
.
[2025-09-05 06:54:19.888][14][debug][http] [source/common/http/conn_manager_impl.cc:1993] [Tags: "ConnectionId":"10743666436109182153","StreamId":"5702234508651400730"] Codec completed encoding stream.
```

Call Stack:
N/A (No crash occurred)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.