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 haproxyCreate 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/mapsCreate 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_backendCreate 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_backendCreate 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_backendCreate 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 falseConfigure 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 TRUECreate 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 trueCreate blocked agents and IPs files
Create files for blocking malicious user agents and IP addresses.
BadBot
MaliciousScanner
SQLInject
XSSAttack
Nikto
w3af
sqlmap198.51.100.100
198.51.100.101
203.0.113.200Set 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.cfgCreate 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/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 -cEnable and start HAProxy
Enable HAProxy to start on boot and start the service.
sudo systemctl enable haproxy
sudo systemctl start haproxy
sudo systemctl status haproxyAdvanced 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_countryA/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_cookieCanary 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_userDynamic 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.sockCreate 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.shSSL 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_backendCreate 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_backendVerify 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 -fPerformance 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 300Monitor 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_mappedCommon issues
| Symptom | Cause | Fix |
|---|---|---|
| ACL always returns false | Incorrect map file format or permissions | Check map file syntax with haproxy -f /etc/haproxy/haproxy.cfg -c |
| Map changes not taking effect | Map not reloaded after changes | Use echo "clear map /path/to/map" | socat stdio /run/haproxy/admin.sock |
| SNI routing not working | Missing SSL certificates or wrong path | Verify certificates in /etc/ssl/certs/haproxy/ with correct permissions |
| Rate limiting not working | Stick table configuration issue | Check stick table definition and track-sc0 statements |
| Backend selection incorrect | ACL precedence order wrong | Reorder use_backend rules, most specific first |
| High CPU usage | Complex regex ACLs processing | Optimize 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_agentsNext steps
- Configure HAProxy multi-site SSL termination with SNI for secure load balancing
- Set up NGINX monitoring with Prometheus and Grafana for web server observability
- Configure HAProxy SSL termination with Let's Encrypt and security headers
- Implement HAProxy rate limiting and DDoS protection with advanced security rules
- Configure HAProxy with Consul for dynamic service discovery and automatic backend updates
- Monitor HAProxy and Consul with Prometheus and Grafana dashboards
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Default configuration
DEFAULT_DOMAIN="example.com"
DOMAIN="${1:-$DEFAULT_DOMAIN}"
# Usage message
usage() {
echo "Usage: $0 [domain]"
echo " domain: Primary domain for HAProxy configuration (default: $DEFAULT_DOMAIN)"
exit 1
}
# Logging functions
log_info() {
echo -e "${GREEN}$1${NC}"
}
log_warn() {
echo -e "${YELLOW}$1${NC}"
}
log_error() {
echo -e "${RED}$1${NC}"
}
# Check if running as root or with sudo
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root or with sudo"
exit 1
fi
# Cleanup function for rollback
cleanup() {
log_warn "Error occurred. Cleaning up..."
systemctl stop haproxy 2>/dev/null || true
rm -rf /etc/haproxy/maps 2>/dev/null || true
}
trap cleanup ERR
# Auto-detect distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update"
HAPROXY_USER="haproxy"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
HAPROXY_USER="haproxy"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
HAPROXY_USER="haproxy"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
else
log_error "Cannot detect distribution"
exit 1
fi
log_info "[1/8] Updating package repositories..."
$PKG_UPDATE
log_info "[2/8] Installing HAProxy..."
$PKG_INSTALL haproxy
log_info "[3/8] Creating HAProxy maps directory..."
mkdir -p /etc/haproxy/maps
chown $HAPROXY_USER:$HAPROXY_USER /etc/haproxy/maps
chmod 755 /etc/haproxy/maps
log_info "[4/8] Creating domain-to-backend map file..."
cat > /etc/haproxy/maps/domains.map << EOF
$DOMAIN web_backend
api.$DOMAIN api_backend
admin.$DOMAIN admin_backend
staging.$DOMAIN staging_backend
beta.$DOMAIN beta_backend
EOF
log_info "[5/8] Creating path-based routing map..."
cat > /etc/haproxy/maps/paths.map << EOF
^/api/ api_backend
^/admin/ admin_backend
^/static/ static_backend
^/upload/ upload_backend
^/webhooks/ webhook_backend
EOF
log_info "[6/8] Creating user agent routing map..."
cat > /etc/haproxy/maps/useragent.map << EOF
Mobile mobile_backend
Android mobile_backend
iPhone mobile_backend
iPad mobile_backend
Bot bot_backend
curl api_backend
EOF
log_info "[7/8] Creating maintenance mode map..."
cat > /etc/haproxy/maps/maintenance.map << EOF
$DOMAIN false
api.$DOMAIN false
admin.$DOMAIN false
staging.$DOMAIN true
beta.$DOMAIN false
EOF
# Set proper permissions for map files
chown $HAPROXY_USER:$HAPROXY_USER /etc/haproxy/maps/*
chmod 644 /etc/haproxy/maps/*
log_info "[8/8] Configuring HAProxy with advanced ACLs..."
cat > /etc/haproxy/haproxy.cfg << 'EOF'
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
defaults
mode http
log global
option httplog
option dontlognull
option forwardfor
timeout connect 5000
timeout client 50000
timeout server 50000
frontend web_frontend
bind *:80
# ACL definitions using map files
acl domain_maintenance map(/etc/haproxy/maps/maintenance.map) true
acl is_api_path path_beg /api/
acl is_admin_path path_beg /admin/
acl is_static_path path_beg /static/
acl is_mobile_agent hdr_sub(User-Agent) -i Mobile Android iPhone iPad
acl is_bot_agent hdr_sub(User-Agent) -i bot crawler spider
# Routing decisions
use_backend maintenance_backend if domain_maintenance
use_backend api_backend if is_api_path
use_backend admin_backend if is_admin_path
use_backend static_backend if is_static_path
use_backend mobile_backend if is_mobile_agent
use_backend bot_backend if is_bot_agent
default_backend web_backend
backend web_backend
balance roundrobin
server web1 127.0.0.1:8080 check
server web2 127.0.0.1:8081 check
backend api_backend
balance roundrobin
server api1 127.0.0.1:8082 check
server api2 127.0.0.1:8083 check
backend admin_backend
balance roundrobin
server admin1 127.0.0.1:8084 check
backend static_backend
balance roundrobin
server static1 127.0.0.1:8085 check
backend mobile_backend
balance roundrobin
server mobile1 127.0.0.1:8086 check
backend bot_backend
balance roundrobin
server bot1 127.0.0.1:8087 check
backend maintenance_backend
http-request return status 503 content-type text/html string "Service temporarily unavailable"
listen stats
bind *:8404
stats enable
stats uri /
stats refresh 30s
stats admin if TRUE
EOF
# Validate HAProxy configuration
if ! haproxy -f /etc/haproxy/haproxy.cfg -c; then
log_error "HAProxy configuration validation failed"
exit 1
fi
# Enable and start HAProxy service
systemctl enable haproxy
systemctl restart haproxy
# Configure firewall based on distribution
if command -v ufw >/dev/null 2>&1; then
ufw allow 80/tcp
ufw allow 8404/tcp
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=8404/tcp
firewall-cmd --reload
fi
# Verification checks
log_info "Verifying installation..."
if systemctl is-active --quiet haproxy; then
log_info "✓ HAProxy service is running"
else
log_error "✗ HAProxy service failed to start"
exit 1
fi
if [ -d /etc/haproxy/maps ] && [ -f /etc/haproxy/maps/domains.map ]; then
log_info "✓ HAProxy maps directory and files created"
else
log_error "✗ HAProxy maps not properly created"
exit 1
fi
log_info "HAProxy advanced routing setup completed successfully!"
log_info "Configuration file: /etc/haproxy/haproxy.cfg"
log_info "Maps directory: /etc/haproxy/maps"
log_info "Stats interface: http://localhost:8404"
log_warn "Note: Update backend server addresses in /etc/haproxy/haproxy.cfg for your environment"
Review the script before running. Execute with: bash install.sh