HAProxy “use_backend” Rule Evaluation Order: Understanding ACL and Backend Selection Priority


2 views

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)]