Skip to content

HTTP/1.1 Frontend connections fail when QUIC is enabled in backend section #3267

@alex1989hu

Description

@alex1989hu

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

  1. Enable QUIC on frontend and backend
  2. Send a request with cURL by using --http1.1 option:
$ 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-auto

Output 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: needs-triageThis issue needs to be triaged.type: bugThis issue describes a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions