Skip to content

acl method misbehaves when multiple methods are defined #3060

@hryamzik

Description

@hryamzik

Detailed Description of the Problem

Let's say we have the following acl definition:

acl acl1 path -m beg /one/  -m str /one

It should match all the requests coming to /one/<,,,> and /one exactly, and nothing else. However in this case str method stops working as an exact match and request to /onetwothree/oopsie still matches this ACL!

If I swap methods, subpaths of /one/ stop working:

acl acl1 path  -m str /one -m beg /one/

Expected Behavior

Only requests to /one and /one/ subpaths work.

Steps to Reproduce the Behavior

haproxy.conf: see below.

Docker-compose.yml:

services:
  haproxy:
    image: haproxy:3.2.3
    container_name: haproxy
    ports:
      - "80:80"
      - "443:443"
      - "8404:8404"
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
    restart: unless-stopped

and curl command:

curl http://localhost/onetwothree/oopsie

Do you have any idea what may have caused this?

Using multiple methods doesn't seem to be well documented. There's some evidence only last one should work, but that's not true.

Do you have an idea how to solve the issue?

No response

What is your configuration?

global
    log stdout format raw daemon

defaults
    log     global
    mode    http
    option  httplog
    option  http-server-close
    timeout connect 5s
    timeout client  30s
    timeout server  30s

frontend http
    bind *:80
    mode http

    # acl acl1 path -m str /one -m beg /one/    
    acl acl1 path -m beg /one/    -m str /one
    acl acl2 path -m beg /onetwo/ -m str /onetwo
    # acl acl1 path -m beg /one/
    # acl acl2 path -m beg /onetwo/
    # acl acl1 path -m str /one
    # acl acl2 path -m str /onetwo

    # acl acl3 path -m beg /three/ -m str /tri
    acl acl3 path -m str /tri -m beg /three/
    # acl acl3 path -m str /tri

    use_backend backend1 if acl1
    use_backend backend2 if acl2
    use_backend backend3 if acl3

backend backend1
    mode http
    http-request return status 200 content-type "text/plain" string "Hello from backend ONE\n"

backend backend2
    mode http
    http-request return status 200 content-type "text/plain" string "Hello from backend ONETWO\n"

backend backend3
    mode http
    http-request return status 200 content-type "text/plain" string "Hello from backend 3\n"

Output of haproxy -vv

HAProxy version 3.2.3-1844da7 2025/07/09 - 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.3.html
Running on: Linux 6.14.10-orbstack-00291-g1b252bd3edea #1 SMP Sat Jun  7 02:45:18 UTC 2025 aarch64
Build options : 
  TARGET  = linux-glibc
  CC      = cc
  CFLAGS  = -O2 -g -fwrapv
  OPTIONS = USE_GETADDRINFO=1 USE_OPENSSL=1 USE_LUA=1 USE_PROMEX=1 USE_PCRE2=1 USE_PCRE2_JIT=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

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.0.16 11 Feb 2025
Running on SSL library version : OpenSSL 3.0.16 11 Feb 2025
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.4
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.42 2022-12-11
PCRE2 library supports JIT : yes
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

Last Outputs and Backtraces


Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: works as designedThis issue stems from a misunderstanding of how HAProxy is supposed to work.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions