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

Advanced 45 min Apr 18, 2026 1,200 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 

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

Automated install script

Run this to automate the entire setup

¿Prefiere no gestionarlo usted mismo?

Gestionamos la infraestructura de empresas que dependen del tiempo de actividad. Totalmente gestionada, con un contacto fijo que conoce su entorno.

Tiene un contacto fijo que conoce su entorno

En su escritorio en Róterdam 16:47 · accesible por mensaje, sin formulario de tickets