This episode masters the art of content switching: advanced ACL expressions, combining host, path, header, and payload conditions into a single decision, and structuring complex frontend logic that stays easy to understand.

Content switching is the ability to decide a destination based on the request's contents — not just IP and port. This is where ACLs evolve from simple conditions into a complete logical language.
Episode 16 covers advanced ACL expressions, how to combine many conditions into a single decision, and how to structure strong frontend logic without making the configuration file hard to read.
ACLs can process raw values with transformations. Example: comparing the API version from the path:
frontend api_front
bind *:443 ssl crt /etc/haproxy/certs/fullchain.pem
mode http
acl api_v1 path_reg ^/v1/
acl api_v2 path_reg ^/v2/
acl is_json req.hdr(Content-Type) -i application/json
use_backend v1_back if api_v1
use_backend v2_back if api_v2
default_backend v1_backacl api_v1 path_reg ^/v1/ uses a regex to match a path prefix. Regexes give a flexibility path_beg can't offer.
Some decisions require looking at the beginning of the request body:
frontend api_front
bind *:80
mode http
acl req_payload_ok req.payload(0,32) -m reg \
"^\s*\{\s*\"name\""
http-request deny deny_status 400 if !req_payload_ok
default_backend api_backreq.payload(0,32) -m reg takes the first 32 bytes of the body and matches them against a JSON pattern. This is an example of content switching based on real contents.
Chained transformations allow finer logic:
frontend web_front
bind *:80
mode http
acl high_priority hdr(priority),lower -m str high
acl small_room hdr(x-body-size),int gt 10000
http-request deny deny_status 413 if !high_priority small_room
default_backend web_backhdr(priority),lower -m str high lowercases the header, then compares it; hdr(x-body-size),int gt 10000 converts the header value to an integer before comparing.
Conditions in use_backend and http-request support or, and, and !:
frontend web_front
bind *:80
mode http
acl host_app1 hdr(host) -i app1.example.com
acl host_app2 hdr(host) -i app2.example.com
acl is_api path_beg /api
acl is_vip hdr(x-client-type) -i premium
use_backend premium_back if is_vip and is_api
use_backend app1_back if host_app1 and !is_api
use_backend app2_back if host_app2
default_backend web_backuse_backend premium_back if is_vip and is_api picks the premium backend only when both conditions are true at once. The top-to-bottom ordering means premium requests are always checked first.
Multiple lines with the same ACL name are automatically joined with OR:
frontend web_front
bind *:80
mode http
acl is_admin hdr(host) -i admin.example.com
acl is_admin src 10.0.0.0/8
acl is_admin path_beg /internal
http-request deny deny_status 404 if is_admin path_beg /public
default_backend web_backBecause is_admin is defined three times with different meanings, that ACL is true if any one of its definitions matches. This is a neat way to combine many possibilities.
Complex frontend logic must still stay readable by humans. The rules:
use_backend from the most specific.default_backend fallback at the very bottom.frontend api_front
bind *:443 ssl crt /etc/haproxy/certs/fullchain.pem
mode http
option httplog
acl has_key req.hdr(X-Api-Key) -m found
acl is_v1 path_beg /v1
acl is_v2 path_beg /v2
acl is_internal src 10.0.0.0/8
http-request deny deny_status 401 if !has_key !is_internal
http-request deny deny_status 405 if !is_v1 !is_v2
use_backend v2_back if is_v2 has_key
default_backend v1_backhttp-request deny deny_status 405 if !is_v1 !is_v2 rejects paths that aren't v1 or v2 before routing ever happens. This ordering discipline is what separates maintainable configuration from chaos.
Pitfalls that often appear as ACLs get more complex:
X-Forwarded-For yourself.path_reg with several real requests.default_backend go missing so unrouted traffic gets rejected.curl -s -o /dev/null -w "%{http_code}\n" \
-H "X-Api-Key: test" https://localhost/v2/health -k
curl -s -o /dev/null -w "%{http_code}\n" \
https://localhost/v2/health -kcurl -s -o /dev/null -w "%{http_code}\n" with and without the X-Api-Key header verifies that the deny rules work as expected.
Episode 16 turns ACLs from simple if-then into a complete logical language: regexes, value conversions, payloads, and condition combinations. With ordering discipline, complex frontends stay maintainable.
Key takeaways:
path_reg, req.payload, and value transforms extend ACL expressions.and, or, and ! in a single decision.default_backend and test the logic with real requests.In the next episode we'll cover the HAProxy Enterprise features overview — the flagship features of the Enterprise edition, the event-driven architecture and additional modules, and an honest feature set comparison between the Community and Enterprise editions.