-
Notifications
You must be signed in to change notification settings - Fork 903
Description
Detailed Description of the Problem
Dear HAProxy Maintainers!
I understand that connect to backend servers with HTTP/3 over QUIC is an experimental feature, but I would like to share some feedback.
When a client attempts to connect (to frontend) using HTTP/1.1, the connection fails. Clients such as cURL, Bruno, and Postman remain stuck after connection has been established, and after approximately 30 seconds, HAProxy returns a 502 Bad Gateway error.
Expected Behavior
HTTP/1.1, HTTP/2 and HTTP/3 should work if we use QUIC for backend connections.
Steps to Reproduce the Behavior
- Enable QUIC on frontend and backend
- Send a request with cURL by using
--http1.1option:
$ curl --http1.1 -v https://host
* Host host:443 was resolved.
* IPv6: (none)
* IPv4: 1.2.3.4, 5.6.7.8
* Trying 1.2.3.4:443...
* ALPN: curl offers http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL Trust Anchors:
* Native: Apple SecTrust
* OpenSSL default paths (fallback)
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519MLKEM768 / id-ecPublicKey
* ALPN: server accepted http/1.1
* Server certificate:
* subject: CN=host
* start date: Feb 1 18:00:35 2026 GMT
* expire date: May 2 18:00:35 2026 GMT
* issuer: <redacted>
* Certificate level 0: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
* subjectAltName: "host" matches cert's "host"
* SSL certificate verified via Apple SecTrust.
* Established connection to host (1.2.3.4 port 443) from 2.3.4.5 port 51871
* using HTTP/1.x
> GET / HTTP/1.1
> Host: host
> User-Agent: curl/8.18.0
> Accept: */*
>
* Request completely sent off
# ^^^^^^^ This is where we stuck for 30s
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/1.1 502 Bad Gateway
< content-length: 107
< cache-control: no-cache
< content-type: text/html
<
<html><body><h1>502 Bad Gateway</h1>
The server returned an invalid or incomplete response.
</body></html>
* Connection #0 to host host:443 left intact
Do you have any idea what may have caused this?
No response
Do you have an idea how to solve the issue?
No response
What is your configuration?
global
log stdout len 65335 format raw local0 debug
daemon
dns-accept-family ipv4
expose-experimental-directives
maxconn 100000
maxsslrate 5000
stats socket /var/lib/haproxy/admin.sock mode 660 level admin
ssl-default-bind-curves X25519MLKEM768:prime256v1:secp384r1:secp521r1
ssl-default-bind-options ssl-min-ver TLSv1.2 strict-sni
ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-server-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-curves X25519MLKEM768:prime256v1:secp384r1:secp521r1
ssl-default-server-options ssl-min-ver TLSv1.3
defaults
log global
option httplog
retries 2
option redispatch
timeout check 3s
timeout client 75s
timeout connect 5s
timeout server 300s
frontend frontend
mode http
bind *:8443 ssl crt /usr/local/etc/cert/tls-combined.pem tls-ticket-keys /usr/local/etc/tls-ticket/tls.ticket.key strict-sni
# The behavior remains the same regardless of whether QUIC is enabled on the frontend.
bind quic4@*:8443 ssl crt /usr/local/etc/cert/tls-combined.pem tls-ticket-keys /usr/local/etc/tls-ticket/tls.ticket.key alpn h3 strict-sni
backend backend
mode http
# Only one server line is defined at a time; both are shown here for demonstration purposes.
# NOK: HTTP/1.1 OK: HTTP/2, HTTP/3
server name quic4@server:443 alpn h3 check port 80 check-alpn http/1.1 check-reuse-pool inter 5s rise 2 fall 3 slowstart 15s ssl verify required ca-file @system-ca sni-auto
# Only one server line is defined at a time; both are shown here for demonstration purposes.
# OK: HTTP/1.1, HTTP/2, HTTP/3
server name server:443 alpn h2 check port 80 check-alpn http/1.1 check-reuse-pool inter 5s rise 2 fall 3 slowstart 15s ssl verify required ca-file @system-ca sni-autoOutput of haproxy -vv
$ uname -a
Linux haproxy-nzbp9 6.18.5-talos #1 SMP Tue Jan 20 18:42:30 UTC 2026 x86_64 GNU/Linux
$ haproxy -vv
HAProxy version 3.3.3-465d8e2fc 2026/02/12 - https://haproxy.org/
Status: stable branch - will stop receiving fixes around Q1 2027.
Known bugs: http://www.haproxy.org/bugs/bugs-3.3.3.html
Running on: Linux 6.18.5-talos #1 SMP Tue Jan 20 18:42:30 UTC 2026 x86_64
Build options :
TARGET = linux-glibc
CC = cc
CFLAGS = -O2 -g -fwrapv -fvect-cost-model=very-cheap
OPTIONS = USE_PTHREAD_EMULATION=1 USE_LINUX_TPROXY=1 USE_GETADDRINFO=1 USE_OPENSSL_AWSLC=1 USE_LUA=1 USE_SLZ=1 USE_TFO=1 USE_QUIC=1 USE_PROMEX=1 USE_PCRE2=1 USE_PCRE2_JIT=1
DEBUG =
Feature list : -51DEGREES +ACCEPT4 +BACKTRACE -CLOSEFROM +CPU_AFFINITY +CRYPT_H -DEVICEATLAS +DL -ECH -ENGINE +EPOLL -EVPORTS +GETADDRINFO -KQUEUE +KTLS -LIBATOMIC +LIBCRYPT +LINUX_CAP +LINUX_SPLICE +LINUX_TPROXY +LUA +MATH -MEMORY_PROFILING +NETFILTER +NS -OBSOLETE_LINKER +OPENSSL +OPENSSL_AWSLC -OPENSSL_WOLFSSL -OT -PCRE +PCRE2 +PCRE2_JIT -PCRE_JIT +POLL +PRCTL -PROCCTL +PROMEX +PTHREAD_EMULATION +QUIC -QUIC_OPENSSL_COMPAT +RT +SHM_OPEN +SLZ +SSL -STATIC_PCRE -STATIC_PCRE2 +TFO +THREAD +THREAD_DUMP +TPROXY -WURFL -ZLIB +ACME +HAVE_TCP_MD5SIG
Default settings :
bufsize = 16384, maxrewrite = 1024, maxpollevents = 200
Built with multi-threading support (MAX_TGROUPS=32, MAX_THREADS=1024, default=8).
Built with SSL library version : OpenSSL 1.1.1 (compatible; AWS-LC 1.65.1)
Running on SSL library version : AWS-LC 1.65.1
SSL library supports TLS extensions : yes
SSL library supports SNI : yes
SSL library FIPS mode : no
SSL library supports : TLSv1.0 TLSv1.1 TLSv1.2 TLSv1.3
QUIC: connection sock-per-conn mode support : yes
QUIC: GSO emission support : yes
Built with Lua version : Lua 5.4.7
Built with the Prometheus exporter as a service
Built with network namespace support.
Built with libslz for stateless compression.
Compression algorithms supported : identity("identity"), deflate("deflate"), raw-deflate("deflate"), gzip("gzip")
Built with transparent proxy support using: IP_TRANSPARENT IPV6_TRANSPARENT IP_FREEBIND
Built with PCRE2 version : 10.46 2025-08-27
PCRE2 library supports JIT : yes
Encrypted password support via crypt(3): yes
Built with gcc compiler version 14.2.0
Available polling systems :
epoll : pref=300, test result OK
poll : pref=200, test result OK
select : pref=150, test result OK
Total: 3 (3 usable), will use epoll.
Available multiplexer protocols :
(protocols marked as <default> cannot be specified using 'proto' keyword)
quic : mode=HTTP side=FE|BE mux=QUIC flags=HTX|NO_UPG|FRAMED
h2 : mode=HTTP side=FE|BE mux=H2 flags=HTX|HOL_RISK|NO_UPG
<default> : mode=HTTP side=FE|BE mux=H1 flags=HTX
h1 : mode=HTTP side=FE|BE mux=H1 flags=HTX|NO_UPG
fcgi : mode=HTTP side=BE mux=FCGI flags=HTX|HOL_RISK|NO_UPG
<default> : mode=SPOP side=BE mux=SPOP flags=HOL_RISK|NO_UPG
spop : mode=SPOP side=BE mux=SPOP flags=HOL_RISK|NO_UPG
<default> : mode=TCP side=FE|BE mux=PASS flags=
none : mode=TCP side=FE|BE mux=PASS flags=NO_UPG
Available services : prometheus-exporter
Available filters :
[BWLIM] bwlim-in
[BWLIM] bwlim-out
[CACHE] cache
[COMP] compression
[FCGI] fcgi-app
[SPOE] spoe
[TRACE] trace
Last Outputs and Backtraces
HAProxy produces only a single log entry. After the connection times out, one log entry appears:
<redacted> [12/Feb/2026:16:54:05.628] frontend/13: Connection closed during SSL handshake
Additional Information
I use container images: haproxytech/haproxy-debian:3.3.2, haproxytech/haproxy-debian:3.3.3. Both versions are affected.
To rule out a backend server issue, I verified that I can successfully retrieve a response from within the HAProxy container using curl over all three protocols: --http1.1, --http2, --http3-only.