Configure HAProxy advanced routing with ACLs and maps for intelligent traffic management

Advanced 45 min Apr 18, 2026 160 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up sophisticated traffic routing in HAProxy using Access Control Lists (ACLs) and map files for dynamic backend selection, SSL SNI routing, and intelligent request distribution based on headers, paths, and custom conditions.

Prerequisites

  • HAProxy 2.4+
  • Root or sudo access
  • Basic understanding of HTTP headers
  • SSL certificates for SNI routing

What this solves

HAProxy's advanced routing capabilities let you intelligently distribute traffic based on custom conditions like URL paths, HTTP headers, SSL SNI domains, or geographic regions. This tutorial covers Access Control Lists (ACLs) for complex routing logic and map files for dynamic backend selection without configuration reloads.

You'll need this when running multiple applications behind a single load balancer, implementing A/B testing, routing based on user agents, or managing multi-tenant applications with domain-based routing.

Step-by-step configuration

Install HAProxy

Install HAProxy and verify the version supports advanced ACL features.

sudo apt update
sudo apt install -y haproxy
sudo dnf install -y haproxy

Create map files directory

Create a directory for map files that will store dynamic routing rules.

sudo mkdir -p /etc/haproxy/maps
sudo chown haproxy:haproxy /etc/haproxy/maps
sudo chmod 755 /etc/haproxy/maps

Create domain-to-backend map file

Create a map file that associates domains with specific backend servers for SNI routing.

# Domain to backend mapping
example.com web_backend
api.example.com api_backend
admin.example.com admin_backend
staging.example.com staging_backend
beta.example.com beta_backend

Create path-based routing map

Create a map file for path-based routing to different application backends.

# Path prefix to backend mapping
^/api/ api_backend
^/admin/ admin_backend
^/static/ static_backend
^/upload/ upload_backend
^/webhooks/ webhook_backend

Create user agent routing map

Create a map file for routing based on user agent strings for mobile/desktop separation.

# User agent patterns to backend mapping
Mobile mobile_backend
Android mobile_backend
iPhone mobile_backend
iPad mobile_backend
Bot bot_backend
curl api_backend

Create maintenance mode map

Create a map file for maintenance mode control per domain or path.

# Domain/path maintenance status
example.com false
api.example.com false
admin.example.com false
staging.example.com true
beta.example.com false

Configure HAProxy with advanced ACLs

Create the main HAProxy configuration with sophisticated ACL rules and map file integration.

global
    log stdout local0 info
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon
    ssl-default-bind-ciphers ECDHE+aRSA+AES256+GCM+SHA384:ECDHE+aRSA+CHACHA20:ECDHE+aRSA+AES128+GCM+SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
    tune.ssl.default-dh-param 2048

defaults
    mode http
    log global
    option httplog
    option dontlognull
    option log-health-checks
    option forwardfor
    option httpchk
    timeout connect 5000
    timeout client 50000
    timeout server 50000
    timeout http-request 10s
    timeout http-keep-alive 2s
    timeout check 10s
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 408 /etc/haproxy/errors/408.http
    errorfile 500 /etc/haproxy/errors/500.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http

frontend web_frontend
    bind *:80
    bind *:443 ssl crt /etc/ssl/certs/haproxy/
    
    # Redirect HTTP to HTTPS
    redirect scheme https code 301 if !{ ssl_fc }
    
    # Load map files
    # Domain-based routing
    acl is_domain_mapped map(/etc/haproxy/maps/domains.map) -m found
    acl domain_backend map(/etc/haproxy/maps/domains.map) -m str
    
    # Path-based routing
    acl is_api_path path_beg /api/
    acl is_admin_path path_beg /admin/
    acl is_static_path path_beg /static/
    acl is_upload_path path_beg /upload/
    acl is_webhook_path path_beg /webhooks/
    
    # User agent based routing
    acl is_mobile_agent hdr_sub(User-Agent) -i -f /etc/haproxy/maps/useragent.map
    acl is_bot_agent hdr_sub(User-Agent) -i bot crawler spider
    acl is_curl_agent hdr_beg(User-Agent) -i curl
    
    # Geographic and IP-based ACLs
    acl is_internal_ip src 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
    acl is_office_ip src 203.0.113.0/24
    acl is_cdn_ip src 198.51.100.0/24
    
    # HTTP method and header ACLs
    acl is_post_method method POST
    acl is_get_method method GET
    acl has_auth_header hdr(Authorization) -m found
    acl is_json_content hdr(Content-Type) -i application/json
    acl is_api_key_valid hdr(X-API-Key) -f /etc/haproxy/maps/valid_api_keys.map
    
    # Time-based ACLs
    acl is_business_hours date +%H %ge 9
    acl is_business_hours date +%H %le 17
    acl is_weekday date +%w %ge 1
    acl is_weekday date +%w %le 5
    acl is_working_hours is_business_hours is_weekday
    
    # SSL SNI ACLs
    acl is_api_sni ssl_fc_sni -i api.example.com
    acl is_admin_sni ssl_fc_sni -i admin.example.com
    acl is_main_sni ssl_fc_sni -i example.com www.example.com
    
    # Maintenance mode checks
    acl is_maintenance map_str(/etc/haproxy/maps/maintenance.map,%[ssl_fc_sni]) true
    acl is_maintenance_path map_str(/etc/haproxy/maps/maintenance.map,%[path]) true
    
    # Rate limiting ACLs
    acl too_many_requests sc0_http_req_rate() gt 20
    acl burst_detected sc0_http_req_rate() gt 50
    
    # Security ACLs
    acl blocked_ua hdr_sub(User-Agent) -i -f /etc/haproxy/blocked_agents.txt
    acl blocked_ip src -f /etc/haproxy/blocked_ips.txt
    acl suspicious_path path_reg -i \.(php|asp|jsp|cgi)$
    acl sql_injection path_reg -i (union|select|insert|delete|drop|create|alter)
    
    # Stick tables for rate limiting
    stick-table type ip size 100k expire 30m store http_req_rate(10s)
    http-request track-sc0 src
    
    # Security rules (highest priority)
    http-request deny if blocked_ip || blocked_ua || sql_injection
    http-request deny if burst_detected
    http-request tarpit if too_many_requests
    
    # Maintenance mode
    http-request redirect location /maintenance.html if is_maintenance !is_internal_ip
    
    # Admin access restrictions
    http-request deny if is_admin_path !is_office_ip !has_auth_header
    http-request deny if is_admin_sni !is_office_ip
    
    # API routing with authentication
    use_backend api_backend if is_api_sni || is_api_path
    use_backend api_backend if is_curl_agent is_api_key_valid
    
    # Mobile traffic routing
    use_backend mobile_backend if is_mobile_agent !is_api_path
    
    # Bot traffic routing
    use_backend bot_backend if is_bot_agent
    
    # Static content routing
    use_backend static_backend if is_static_path || path_end .css .js .png .jpg .gif .ico .woff2
    
    # Upload service routing
    use_backend upload_backend if is_upload_path is_post_method
    
    # Webhook routing
    use_backend webhook_backend if is_webhook_path is_json_content
    
    # Admin panel routing
    use_backend admin_backend if is_admin_sni || (is_admin_path is_office_ip)
    
    # Domain-based routing using maps
    use_backend %[ssl_fc_sni,map(/etc/haproxy/maps/domains.map)] if is_domain_mapped
    
    # Working hours routing (different backend capacity)
    use_backend web_backend_peak if is_working_hours
    
    # Default backend
    default_backend web_backend

Backend definitions

backend web_backend balance roundrobin option httpchk GET /health HTTP/1.1\r\nHost:\ example.com http-check expect status 200 server web1 203.0.113.10:80 check inter 5s fall 3 rise 2 server web2 203.0.113.11:80 check inter 5s fall 3 rise 2 server web3 203.0.113.12:80 check inter 5s fall 3 rise 2 backend web_backend_peak balance roundrobin option httpchk GET /health HTTP/1.1\r\nHost:\ example.com http-check expect status 200 server web1 203.0.113.10:80 check inter 5s fall 3 rise 2 server web2 203.0.113.11:80 check inter 5s fall 3 rise 2 server web3 203.0.113.12:80 check inter 5s fall 3 rise 2 server web4 203.0.113.13:80 check inter 5s fall 3 rise 2 server web5 203.0.113.14:80 check inter 5s fall 3 rise 2 backend api_backend balance leastconn option httpchk GET /api/health HTTP/1.1\r\nHost:\ api.example.com http-check expect status 200 http-request add-header X-Forwarded-Proto https server api1 203.0.113.20:8080 check inter 3s fall 2 rise 2 server api2 203.0.113.21:8080 check inter 3s fall 2 rise 2 server api3 203.0.113.22:8080 check inter 3s fall 2 rise 2 backend mobile_backend balance roundrobin option httpchk GET /mobile/health HTTP/1.1\r\nHost:\ m.example.com http-check expect status 200 compression algo gzip compression type text/html text/css application/javascript server mobile1 203.0.113.30:80 check inter 5s server mobile2 203.0.113.31:80 check inter 5s backend admin_backend balance source option httpchk GET /admin/health HTTP/1.1\r\nHost:\ admin.example.com http-check expect status 200 http-request add-header X-Admin-Access true server admin1 203.0.113.40:80 check inter 10s server admin2 203.0.113.41:80 check inter 10s backup backend static_backend balance roundrobin option httpchk GET /static/health.txt http-check expect status 200 http-response set-header Cache-Control "public, max-age=3600" server static1 203.0.113.50:80 check inter 30s server static2 203.0.113.51:80 check inter 30s server cdn1 203.0.113.60:80 check inter 30s backup backend upload_backend balance leastconn option httpchk GET /upload/health HTTP/1.1\r\nHost:\ upload.example.com http-check expect status 200 timeout server 300s server upload1 203.0.113.70:80 check inter 10s server upload2 203.0.113.71:80 check inter 10s backend webhook_backend balance roundrobin option httpchk GET /webhooks/health HTTP/1.1\r\nHost:\ webhooks.example.com http-check expect status 200 http-request add-header X-Webhook-Source haproxy server webhook1 203.0.113.80:80 check inter 5s server webhook2 203.0.113.81:80 check inter 5s backend bot_backend balance roundrobin option httpchk GET /robots.txt http-check expect status 200 rate-limit sessions 10 server bot1 203.0.113.90:80 check inter 30s backend staging_backend balance roundrobin option httpchk GET /health HTTP/1.1\r\nHost:\ staging.example.com http-check expect status 200 server staging1 203.0.113.100:80 check inter 10s backend beta_backend balance roundrobin option httpchk GET /health HTTP/1.1\r\nHost:\ beta.example.com http-check expect status 200 server beta1 203.0.113.110:80 check inter 10s

Statistics interface

listen stats bind *:8404 stats enable stats uri /stats stats refresh 30s stats admin if TRUE

Create API key validation map

Create a map file for valid API keys that can access protected endpoints.

# Valid API keys for authentication
sk-1234567890abcdef true
sk-fedcba0987654321 true
sk-api-key-production true
sk-api-key-staging true

Create blocked agents and IPs files

Create files for blocking malicious user agents and IP addresses.

BadBot
MaliciousScanner
SQLInject
XSSAttack
Nikto
w3af
sqlmap
198.51.100.100
198.51.100.101
203.0.113.200

Set proper file permissions

Ensure HAProxy can read all configuration files and map files.

sudo chown haproxy:haproxy /etc/haproxy/maps/*
sudo chown haproxy:haproxy /etc/haproxy/blocked_*
sudo chmod 644 /etc/haproxy/maps/*
sudo chmod 644 /etc/haproxy/blocked_*
sudo chmod 644 /etc/haproxy/haproxy.cfg

Create SSL certificate directory

Create the directory structure for SSL certificates used in SNI routing.

sudo mkdir -p /etc/ssl/certs/haproxy
sudo chown haproxy:haproxy /etc/ssl/certs/haproxy
sudo chmod 700 /etc/ssl/certs/haproxy
Note: Place your SSL certificates in /etc/ssl/certs/haproxy/ as combined PEM files (certificate + private key). Each file should be named after the domain (e.g., example.com.pem).

Validate configuration

Test the HAProxy configuration for syntax errors before starting the service.

sudo haproxy -f /etc/haproxy/haproxy.cfg -c

Enable and start HAProxy

Enable HAProxy to start on boot and start the service.

sudo systemctl enable haproxy
sudo systemctl start haproxy
sudo systemctl status haproxy

Advanced ACL patterns and use cases

Geographic routing with GeoIP

Configure geographic-based routing using GeoIP databases for region-specific backends.

# GeoIP-based routing (requires GeoIP database)
acl is_eu_country src_geoip_country -f /etc/haproxy/eu_countries.txt
acl is_us_country src_geoip_country -f /etc/haproxy/us_countries.txt
acl is_asia_country src_geoip_country -f /etc/haproxy/asia_countries.txt

Route to regional backends

use_backend eu_backend if is_eu_country use_backend us_backend if is_us_country use_backend asia_backend if is_asia_country

A/B testing with percentage-based routing

Implement A/B testing by routing a percentage of traffic to different backends.

# A/B testing - route 20% to version B
acl is_test_user rand(100) lt 20
acl has_ab_cookie hdr_sub(Cookie) ab_test=version_b

Route test traffic

use_backend web_backend_v2 if is_test_user || has_ab_cookie

Canary deployments with header-based routing

Route traffic to canary deployments based on special headers or user segments.

# Canary deployment routing
acl is_canary_user hdr(X-Canary-User) -i true
acl is_beta_tester hdr_sub(Cookie) beta_tester=true
acl is_internal_user hdr(X-Internal-User) -i true

Route canary traffic

use_backend canary_backend if is_canary_user || is_beta_tester || is_internal_user

Dynamic map management

Hot-reload map files without downtime

Update map files and reload them without restarting HAProxy using the stats socket.

# Add new domain mapping
echo "newdomain.example.com web_backend" | sudo tee -a /etc/haproxy/maps/domains.map

Reload the map file via stats socket

echo "clear map /etc/haproxy/maps/domains.map" | sudo socat stdio /run/haproxy/admin.sock echo "show map /etc/haproxy/maps/domains.map" | sudo socat stdio /run/haproxy/admin.sock

Create map management script

Create a script to manage map files dynamically with validation and hot-reload.

#!/bin/bash

HAProxy Map Manager Script

MAP_DIR="/etc/haproxy/maps" SOCK_FILE="/run/haproxy/admin.sock" function add_domain_mapping() { local domain=$1 local backend=$2 local map_file="$MAP_DIR/domains.map" if [ -z "$domain" ] || [ -z "$backend" ]; then echo "Usage: add_domain_mapping " exit 1 fi # Check if domain already exists if grep -q "^$domain " "$map_file"; then echo "Domain $domain already exists in map" exit 1 fi # Add new mapping echo "$domain $backend" >> "$map_file" # Reload map echo "clear map $map_file" | socat stdio $SOCK_FILE echo "Domain $domain added and map reloaded" } function remove_domain_mapping() { local domain=$1 local map_file="$MAP_DIR/domains.map" if [ -z "$domain" ]; then echo "Usage: remove_domain_mapping " exit 1 fi # Remove domain from map sed -i "/^$domain /d" "$map_file" # Reload map echo "clear map $map_file" | socat stdio $SOCK_FILE echo "Domain $domain removed and map reloaded" } function list_mappings() { local map_file=$1 if [ -z "$map_file" ]; then echo "Available map files:" ls -1 $MAP_DIR/*.map exit 0 fi echo "show map $MAP_DIR/$map_file" | socat stdio $SOCK_FILE } function toggle_maintenance() { local domain=$1 local status=$2 local map_file="$MAP_DIR/maintenance.map" if [ -z "$domain" ] || [ -z "$status" ]; then echo "Usage: toggle_maintenance " exit 1 fi # Update maintenance status if grep -q "^$domain " "$map_file"; then sed -i "s/^$domain .*/$domain $status/" "$map_file" else echo "$domain $status" >> "$map_file" fi # Reload map echo "clear map $map_file" | socat stdio $SOCK_FILE echo "Maintenance mode for $domain set to $status" } case $1 in add-domain) add_domain_mapping $2 $3 ;; remove-domain) remove_domain_mapping $2 ;; list) list_mappings $2 ;; maintenance) toggle_maintenance $2 $3 ;; *) echo "Usage: $0 {add-domain|remove-domain|list|maintenance}" echo " add-domain " echo " remove-domain " echo " list [map-file]" echo " maintenance " exit 1 ;; esac

Make script executable

Set proper permissions for the map management script.

sudo chmod +x /usr/local/bin/haproxy-map-manager.sh
sudo chown root:haproxy /usr/local/bin/haproxy-map-manager.sh

SSL SNI routing configuration

Advanced SNI routing with wildcard certificates

Configure advanced SNI routing with wildcard certificates and subdomain handling.

# Advanced SNI routing
frontend ssl_frontend
    bind *:443 ssl crt /etc/ssl/certs/haproxy/ crt /etc/ssl/certs/haproxy/wildcard/
    
    # SNI-based ACLs
    acl is_main_domain ssl_fc_sni -i example.com www.example.com
    acl is_api_domain ssl_fc_sni -i api.example.com
    acl is_admin_domain ssl_fc_sni -i admin.example.com
    acl is_wildcard_subdomain ssl_fc_sni -m reg -i ^[^.]+\.example\.com$
    
    # Customer tenant routing
    acl is_tenant_domain ssl_fc_sni -m reg -i ^([^.]+)\.tenants\.example\.com$
    acl tenant_exists map_reg(/etc/haproxy/maps/tenants.map,\1) -m found
    
    # Certificate validation
    acl cert_valid ssl_fc_has_crt
    acl cert_verified ssl_fc_verify eq 0
    
    # Client certificate authentication
    acl has_client_cert ssl_fc_has_crt
    acl client_cert_valid ssl_c_verify eq 0
    
    # Enhanced security for admin
    http-request deny if is_admin_domain !client_cert_valid
    
    # Tenant routing with dynamic backend selection
    use_backend %[ssl_fc_sni,regsub(^([^.]+)\.tenants\.example\.com$,\1),map(/etc/haproxy/maps/tenants.map)] if is_tenant_domain tenant_exists
    
    # Standard SNI routing
    use_backend api_backend if is_api_domain
    use_backend admin_backend if is_admin_domain
    use_backend wildcard_backend if is_wildcard_subdomain
    
    default_backend web_backend

Create tenant mapping file

Create a map file for multi-tenant routing based on subdomain extraction.

# Tenant subdomain to backend mapping
tenant1 tenant1_backend
tenant2 tenant2_backend
acme-corp acme_backend
startup-xyz startup_backend
enterprise enterprise_backend

Verify your setup

Test the advanced routing configuration with various conditions and verify map file functionality.

# Check HAProxy status
sudo systemctl status haproxy

View current map contents

echo "show map /etc/haproxy/maps/domains.map" | sudo socat stdio /run/haproxy/admin.sock

Test domain routing

curl -H "Host: api.example.com" http://localhost/ curl -H "Host: admin.example.com" -H "X-API-Key: sk-1234567890abcdef" http://localhost/

Test path-based routing

curl http://localhost/api/health curl http://localhost/static/style.css

Test user agent routing

curl -A "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)" http://localhost/

View HAProxy statistics

curl http://localhost:8404/stats

Test maintenance mode

sudo /usr/local/bin/haproxy-map-manager.sh maintenance example.com true curl -H "Host: example.com" http://localhost/

Check ACL processing in logs

sudo journalctl -u haproxy -f

Performance optimization

Optimize ACL processing performance

Configure HAProxy for optimal ACL and map file performance with large rule sets.

# Performance optimizations for ACLs
global
    # Increase connection limits
    maxconn 40000
    
    # Optimize for large map files
    tune.maxaccept 500
    tune.maxpollevents 200
    
    # Memory optimizations
    tune.bufsize 32768
    tune.maxrewrite 8192
    
    # Map file caching
    tune.pattern.cache-size 1000000
    
    # SSL optimizations
    tune.ssl.cachesize 100000
    tune.ssl.lifetime 300

Monitor ACL performance

Add performance monitoring for ACL processing and map lookups.

# Performance monitoring
frontend web_frontend
    # Capture timing information
    capture request header Host len 64
    capture request header User-Agent len 128
    
    # Log ACL processing time
    http-request set-var(req.start_time) date()
    http-response set-header X-Processing-Time %[date(),sub(%[var(req.start_time)])]
    
    # Monitor map lookup performance
    http-request set-var(req.map_lookups) int(0)
    http-request set-var(req.map_lookups) var(req.map_lookups),add(1) if is_domain_mapped

Common issues

SymptomCauseFix
ACL always returns falseIncorrect map file format or permissionsCheck map file syntax with haproxy -f /etc/haproxy/haproxy.cfg -c
Map changes not taking effectMap not reloaded after changesUse echo "clear map /path/to/map" | socat stdio /run/haproxy/admin.sock
SNI routing not workingMissing SSL certificates or wrong pathVerify certificates in /etc/ssl/certs/haproxy/ with correct permissions
Rate limiting not workingStick table configuration issueCheck stick table definition and track-sc0 statements
Backend selection incorrectACL precedence order wrongReorder use_backend rules, most specific first
High CPU usageComplex regex ACLs processingOptimize regex patterns, use exact matches where possible

Security best practices

Implement comprehensive logging

Configure detailed logging for security monitoring and ACL debugging.

defaults
    # Enhanced logging for security
    log-tag haproxy-acl
    
    # Capture security-related headers
    capture request header X-Forwarded-For len 64
    capture request header X-Real-IP len 64
    capture request header Referer len 128
    capture response header Set-Cookie len 128
    
    # Log format with ACL information
    option httplog
    log-format "%ci:%cp [%t] %ft %b/%s %Tq/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r ACL:%[var(req.matched_acl)]"

Create security monitoring script

Implement automated security monitoring for blocked requests and suspicious patterns.

#!/bin/bash

HAProxy Security Monitor

LOG_FILE="/var/log/haproxy.log" ALERT_EMAIL="admin@example.com" THRESHOLD=50

Monitor for blocked IPs

function check_blocked_ips() { blocked_count=$(grep -c "denied" $LOG_FILE | tail -1000) if [ "$blocked_count" -gt $THRESHOLD ]; then echo "High number of blocked requests: $blocked_count" | \ mail -s "HAProxy Security Alert" $ALERT_EMAIL fi }

Monitor for SQL injection attempts

function check_sql_injection() { sql_attempts=$(grep -i -c "union\|select\|insert\|delete" $LOG_FILE | tail -1000) if [ "$sql_attempts" -gt 10 ]; then echo "SQL injection attempts detected: $sql_attempts" | \ mail -s "HAProxy SQL Injection Alert" $ALERT_EMAIL fi }

Monitor for unusual user agents

function check_suspicious_agents() { suspicious=$(grep -E "(curl|wget|python|php|bot)" $LOG_FILE | tail -100) if [ ! -z "$suspicious" ]; then echo "Suspicious user agents detected:" | \ mail -s "HAProxy Suspicious Activity" $ALERT_EMAIL fi } check_blocked_ips check_sql_injection check_suspicious_agents

Next steps

Running this in production?

Want this handled for you? Running complex HAProxy routing at scale adds a second layer of work: capacity planning, SSL certificate management, security monitoring, and 24/7 incident response. See how we run infrastructure like this for European SaaS and e-commerce teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle private cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.