Skip to content

HTTP/3: upgrade header forwarded #2928

@pittgi

Description

@pittgi

Detailed Description of the Problem

An HTTP/3 request containing the upgrade header is forwarded without modification.
The upgrade header is not supported in HTTP/3, as RFC 9114 states:

4.5. HTTP Upgrade
HTTP/3 does not support the HTTP Upgrade mechanism (Section 7.8 of [HTTP]) or the 101 (Switching Protocols) informational status code (Section 15.2.2 of [HTTP]).

This could cause issues, esp. with HTTP/1.1 backend servers.

Expected Behavior

If a client sends an upgrade header in a request, it should be either deleted or the request should be considered as malformed and be rejected, since the upgrade header would influence the backend communication. Rejecting is best practice in my opinion.

Steps to Reproduce the Behavior

  1. Setup an environment where haproxy forwards HTTP/3 requests to an HTTP/1.1 backend.
  2. Send a request containing the ugprade header (curl with HTTP/3 does not support this, use something like aioquic.
  3. Observe traffic using wireshark or let backend print the received request.

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?

frontend http_in
	mode http
	bind :80
	bind :443  ssl crt /PATH/TO/KEY alpn h2
	bind quic4@:443 ssl crt /PATH/TO/KEY alpn h3
	http-request redirect scheme https unless { ssl_fc }
	http-after-response add-header alt-svc 'h3=":443"; ma=60'
	default_backend http_backend

backend http_backend
	server backend1 127.0.0.1:8080

Output of haproxy -vv

HAProxy version 3.1.5-076df02 2025/02/20 - https://haproxy.org/
Status: stable branch - will stop receiving fixes around Q1 2026.
Known bugs: http://www.haproxy.org/bugs/bugs-3.1.5.html
Running on: Linux 6.11.0-21-generic #21~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Feb 24 16:52:15 UTC 2 x86_64
Build options :
  TARGET  = linux-glibc
  CC      = cc
  CFLAGS  = -O2 -g -fwrapv
  OPTIONS = USE_OPENSSL=1 USE_LUA=1 USE_ZLIB=1 USE_QUIC=1 USE_PROMEX=1 USE_PCRE=1
  DEBUG   = 

Feature list : -51DEGREES +ACCEPT4 +BACKTRACE -CLOSEFROM +CPU_AFFINITY +CRYPT_H -DEVICEATLAS +DL -ENGINE +EPOLL -EVPORTS +GETADDRINFO -KQUEUE -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

Default settings :
  bufsize = 16384, maxrewrite = 1024, maxpollevents = 200

Built with multi-threading support (MAX_TGROUPS=16, MAX_THREADS=256, default=16).
Built with OpenSSL version : OpenSSL 1.1.1t+quic  7 Feb 2023
Running on OpenSSL version : OpenSSL 1.1.1t+quic  7 Feb 2023
OpenSSL library supports TLS extensions : yes
OpenSSL library supports SNI : yes
OpenSSL library supports : TLSv1.0 TLSv1.1 TLSv1.2 TLSv1.3
Built with Lua version : Lua 5.3.6
Built with the Prometheus exporter as a service
Built with network namespace support.
Built with zlib version : 1.3
Running on zlib version : 1.3
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 PCRE version : 8.39 2016-06-14
Running on PCRE version : 8.39 2016-06-14
PCRE library supports JIT : no (USE_PCRE_JIT not set)
Encrypted password support via crypt(3): yes
Built with gcc compiler version 13.3.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     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

00000000:http_in.accept(0004)=7cc0 from [127.0.0.1:51576] ALPN=h3
00000000:http_in.clireq[7cc0:ffffffff]: POST / HTTP/3.0
00000000:http_in.clihdr[7cc0:ffffffff]: host: localhost:443
00000000:http_in.clihdr[7cc0:ffffffff]: upgrade: test
00000000:http_backend.srvrep[7cc0:0038]: HTTP/1.1 200 OK
00000000:http_backend.srvhdr[7cc0:0038]: content-length: 85
00000000:http_backend.srvhdr[7cc0:0038]: content-type: text/plain
00000000:http_backend.srvcls[ffff:0038]
00000000:http_backend.clicls[ffff:0038]
00000000:http_backend.closed[ffff:0038]

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: fixedThis issue is a now-fixed bug.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