Set-path behaves differently with HTTP/1.1 and HTTP/2 access

Hello,

Please see my configuration:

global
log stderr local0

defaults
log global
mode http
log-format “${HAPROXY_HTTPS_LOG_FMT} %HP”

frontend https
bind 10.0.234.239:8443 ssl crt bundle.pem alpn h2,http/1.1,http/1.0
use_backend noslash if { req.hdr(host),host_only -m reg ^noslash.example.com$ }
use_backend slash if { req.hdr(host),host_only -m reg ^slash.example.com$ }

backend noslash
server noslash-backend 2001:67c:1254:e:9:7250:0:1:9080
http-request set-path noslash%[path,regsub(‘^/$’,‘’)]

backend slash
server slash-backend 2001:67c:1254:e:9:7250:0:1:9080
http-request set-path /slash%[path,regsub(‘^/$’,‘’)]

The backend I use is HTTP/1.1 and shows full request line which is coming to it.

When I access my haproxy with HTTP/1.1 with curl:

curl -k --resolve *:8443:10.0.234.239 ``https://slash.example.com:8443/path`` --http1.1
curl -k --resolve *:8443:10.0.234.239 ``https://noslash.example.com:8443/path`` --http1.1

I have such logs:

<134>Mar 12 10:17:17 haproxy[2674090]: 10.0.234.239:35016 [12/Mar/2026:10:17:17.204] https~ slash/slash-backend 0/0/0/1/1 404 425 - - ---- 1/1/0/0/0 0/0 “GET /path HTTP/1.1” 0/0000000000000000/0/0/0 ``slash.example.com/TLSv1.3/TLS_AES_256_GCM_SHA384`` /path
<134>Mar 12 10:17:28 haproxy[2674090]: 10.0.234.239:41972 [12/Mar/2026:10:17:28.763] https~ noslash/noslash-backend 0/0/0/2/2 404 429 - - ---- 1/1/0/0/0 0/0 “GET /path HTTP/1.1” 0/0000000000000000/0/0/0 ``noslash.example.com/TLSv1.3/TLS_AES_256_GCM_SHA384`` /path

And request line is:

GET /slash/path HTTP/1.1

and

GET noslash/path HTTP/1.1

But when I access my haproxy with HTTP/2.0 with curl:

curl -k --resolve *:8443:10.0.234.239 ``https://slash.example.com:8443/path`` --http2
curl -k --resolve *:8443:10.0.234.239 ``https://noslash.example.com:8443/path`` –http2

having logs:

<134>Mar 12 10:17:33 haproxy[2674090]: 10.0.234.239:41974 [12/Mar/2026:10:17:33.898] https~ slash/slash-backend 0/0/0/1/1 404 425 - - ---- 1/1/0/0/0 0/0 “GET ``https://slash.example.com:8443/path`` HTTP/2.0” 0/0000000000000000/0/0/0 ``slash.example.com/TLSv1.3/TLS_AES_256_GCM_SHA384`` ``https://slash.example.com:8443/path
<134>Mar 12 10:17:39 haproxy[2674090]: 10.0.234.239:57464 [12/Mar/2026:10:17:39.678] https~ noslash/noslash-backend 0/0/1/1/2 404 415 - - ---- 1/1/0/0/0 0/0 “GET ``https://noslash.example.com:8443/path`` HTTP/2.0” 0/0000000000000000/0/0/0 ``noslash.example.com/TLSv1.3/TLS_AES_256_GCM_SHA384`` ``https://noslash.example.com:8443/path

The request line is expected for slash version:

GET /slash/path HTTP/1.1

But for noslash access the path is not manipulated:

GET /path HTTP/1.1

I wonder if it’s is expected behavior. I see no errors in the logs.

Is there some path related limit in HTTP/2?

Or just I found a bug?

Regards,

Łukasz

Forgot:

aproxy -vv
HAProxy version 3.2.12-6011f448e 2026/02/12 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2030.
Known bugs: http://www.haproxy.org/bugs/bugs-3.2.12.html
Running on: Linux 6.1.0-43-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.162-1 (2026-02-08) x86_64
Build options :
  TARGET  = linux-glibc
  CC      = cc
  CFLAGS  = -O2 -m64 -fwrapv -fvect-cost-model=very-cheap
  OPTIONS = USE_OPENSSL=1 USE_LUA=1 USE_ZLIB=1 USE_DL=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 -SLZ +SSL -STATIC_PCRE -STATIC_PCRE2 +TFO +THREAD +THREAD_DUMP +TPROXY -WURFL +ZLIB +ACME

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 3.5.5 27 Jan 2026
Running on SSL library version : OpenSSL 3.5.5 27 Jan 2026
SSL library supports TLS extensions : yes
SSL library supports SNI : yes
SSL library supports : TLSv1.0 TLSv1.1 TLSv1.2 TLSv1.3
OpenSSL providers loaded : default
Built with Lua version : Lua 5.4.8
Built with the Prometheus exporter as a service
Built with network namespace support.
Built with zlib version : 1.3.1
Running on zlib version : 1.3.1
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.45 2021-06-15
Running on PCRE version : 8.45 2021-06-15
PCRE library supports JIT : no (USE_PCRE_JIT not set)
Encrypted password support via crypt(3): yes
Built with gcc compiler version 12.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)
         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

Every HTTP URI begins with a slash.

You are out of HTTP spec which is why you get diverging behaviors.