While configuring HAProxy for a domain routing setup, I encountered an unexpected behavior with backend selection that initially seemed counterintuitive. The configuration looked correct at first glance, but the routing wasn't working as anticipated.
acl url_a path_beg /a
acl dom_eye hdr_dom(host) -i www.mydomin.com
use_backend eye1 if dom_eye
use_backend eye2 if dom_eye url_a
The routing exhibited these patterns:
- Working as expected: www.mydomin.com/a → eye2
- Unexpected behavior: www.mydomin.com/a/b → eye1
After experimentation, I discovered that HAProxy evaluates use_backend
rules in sequential order and uses the first matching rule. This explains why my initial configuration didn't work:
# Problematic order:
use_backend eye1 if dom_eye # Matches ALL domains
use_backend eye2 if dom_eye url_a # Never reached for /a/b because eye1 matched first
The solution is to order rules from most specific to least specific:
acl url_a path_beg /a
acl dom_eye hdr_dom(host) -i www.mydomin.com
# Correct order - specific rules first
use_backend eye2 if dom_eye url_a # Checks both conditions
use_backend eye1 if dom_eye # Fallback for other paths
Here are some additional scenarios that demonstrate the evaluation behavior:
# Example 1: Multiple path patterns
acl api path_beg /api
acl admin path_beg /admin
use_backend api_servers if api
use_backend admin_servers if admin
use_backend default_servers
# Example 2: Combining multiple conditions
acl mobile hdr(User-Agent) -i mobile
acl tablet hdr(User-Agent) -i tablet
use_backend mobile_backend if mobile url_beg /m/
use_backend tablet_backend if tablet
use_backend desktop_backend
- Always place the most specific rules first in the evaluation order
- Use comments to document complex routing logic
- Test with various URL patterns to verify the evaluation order
- Consider using
default_backend
for the catch-all case - For complex routing, evaluate using
http-request set-var
and variables
To troubleshoot backend selection issues:
# Enable debugging in HAProxy
global
log 127.0.0.1 local0 debug
# Then check the logs for ACL evaluation:
tail -f /var/log/haproxy.log | grep -E 'acl=.*match'
When configuring HAProxy, I encountered unexpected behavior with use_backend
directives. Here's the original configuration:
acl url_a path_beg /a
acl dom_eye hdr_dom(host) -i www.mydomin.com
use_backend eye1 if dom_eye
use_backend eye2 if dom_eye url_a
Despite having the url_a
ACL for paths beginning with "/a", requests to www.mydomin.com/a/b
were being routed to eye1
instead of eye2
.
HAProxy processes use_backend
rules in sequential order, and crucially:
- It uses the first matching rule it encounters
- The evaluation stops at the first successful match
- All ACLs in a condition must be true for the rule to match
The working configuration places the more specific rule first:
acl url_a path_beg /a
acl dom_eye hdr_dom(host) -i www.mydomin.com
use_backend eye2 if dom_eye url_a # More specific rule first
use_backend eye1 if dom_eye # Catch-all rule last
Consider these additional examples to understand the pattern:
# Example 1: Path-based routing
acl api_path path_beg /api
acl admin_path path_beg /admin
use_backend api_servers if api_path
use_backend admin_servers if admin_path
use_backend web_servers
# Example 2: Complex conditions
acl mobile_user agent -i mobile
acl desktop_user agent -i -f desktop_agents.lst
acl premium_user hdr(X-Premium) -i true
use_backend mobile_premium if mobile_user premium_user
use_backend mobile_regular if mobile_user
use_backend desktop_premium if desktop_user premium_user
use_backend desktop_regular if desktop_user
- Always place more specific rules before general ones
- HAProxy doesn't perform "best match" evaluation - it stops at first match
- Test your configuration with various request patterns
- Consider using
http-request redirect
for complex routing needs
# Debugging tip: Add this to see evaluation flow
http-request set-header X-Haproxy-Rule %[acl_fetch(url_a)]-%[acl_fetch(dom_eye)]