Configure HAProxy with comprehensive rate limiting, connection throttling, and DDoS protection using stick tables, ACLs, and advanced security rules to protect your applications from malicious traffic and ensure service availability.
Prerequisites
- Root or sudo access
- Basic knowledge of load balancing concepts
- Backend web servers configured
What this solves
HAProxy rate limiting and DDoS protection shields your web applications from traffic spikes, brute force attacks, and distributed denial of service attempts. This tutorial implements advanced security rules using stick tables for connection tracking, ACLs for traffic classification, and tarpit mechanisms to slow down malicious requests while maintaining performance for legitimate users.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you get the latest HAProxy version with security features.
sudo apt update && sudo apt upgrade -y
Install HAProxy
Install HAProxy with all required modules for advanced load balancing and security features.
sudo apt install -y haproxy rsyslog
Create backup of default configuration
Always backup the original configuration before making changes for easy rollback if needed.
sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.backup
Configure HAProxy global settings
Set up global security parameters including SSL tuning, logging, and performance optimizations.
global
log 127.0.0.1:514 local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
# Security settings
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
# Performance tuning
tune.ssl.default-dh-param 2048
tune.bufsize 32768
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
timeout http-request 15s
timeout http-keep-alive 15s
# Security headers
option httplog
option dontlognull
option log-health-checks
option forwardfor
option http-server-close
# Error handling
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
Configure stick tables for rate limiting
Create stick tables to track connection rates, request rates, and abuse patterns by IP address.
# Rate limiting frontend
frontend web_frontend
bind *:80
bind *:443 ssl crt /etc/ssl/certs/example.com.pem
# Redirect HTTP to HTTPS
redirect scheme https if !{ ssl_fc }
# Stick tables for tracking
stick-table type ip size 100k expire 30s store http_req_rate(10s),http_err_rate(10s),conn_rate(10s),sess_cnt
# Track requests per IP
http-request track-sc0 src
# DDoS protection rules
# Block if more than 20 requests per 10 seconds
http-request deny if { sc_http_req_rate(0) gt 20 }
# Block if more than 10 connections per 10 seconds
http-request deny if { sc_conn_rate(0) gt 10 }
# Block if error rate is too high (more than 10 errors in 10s)
http-request deny if { sc_http_err_rate(0) gt 10 }
# Tarpit suspicious traffic (slow response for abusers)
http-request tarpit if { sc_http_req_rate(0) gt 15 } { sc_http_req_rate(0) lt 21 }
# Security headers
http-response set-header X-Frame-Options SAMEORIGIN
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-XSS-Protection "1; mode=block"
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
default_backend web_servers
Create advanced ACL rules
Define Access Control Lists to identify and handle different types of traffic patterns and potential attacks.
# Advanced ACL definitions
frontend web_frontend
# ... previous configuration ...
# ACL for known bad user agents
acl bad_bots hdr_sub(User-Agent) -i "sqlmap" "nikto" "nmap" "masscan" "zmap"
acl bad_bots hdr_sub(User-Agent) -i "python-requests" "curl/" "wget/" unless { src -f /etc/haproxy/whitelist_ips.txt }
# ACL for suspicious request patterns
acl suspicious_path path_beg -i "/admin" "/wp-admin" "/phpmyadmin" "/.env"
acl suspicious_path path_reg -i "\.(php|asp|jsp)$" unless { path_beg "/api/" }
acl sql_injection query -m reg -i ".(union|select|insert|delete|update|drop|create|alter)."
acl xss_attempt query -m reg -i ".(script|javascript|vbscript|onload|onerror)."
# Rate limiting for different endpoints
acl api_path path_beg "/api/"
acl login_path path "/login" "/wp-login.php" "/admin/login"
# Geographic restrictions (if needed)
# acl blocked_countries src -f /etc/haproxy/blocked_countries.txt
# Advanced blocking rules
http-request deny if bad_bots
http-request deny if sql_injection
http-request deny if xss_attempt
http-request tarpit if suspicious_path
# Stricter rate limiting for login endpoints
http-request deny if login_path { sc_http_req_rate(0) gt 5 }
# More permissive for API endpoints (but still limited)
http-request deny if api_path { sc_http_req_rate(0) gt 50 }
Configure backend servers with health checks
Set up backend server pools with health monitoring and failover capabilities.
backend web_servers
balance roundrobin
option httpchk GET /health HTTP/1.1\r\nHost:\ example.com
# Backend server health and performance settings
default-server check inter 3000ms rise 2 fall 3 maxconn 100
# Add your backend servers
server web1 10.0.1.10:80 check
server web2 10.0.1.11:80 check
server web3 10.0.1.12:80 check backup
# Connection limits per server
server web1 10.0.1.10:80 maxconn 200 check
server web2 10.0.1.11:80 maxconn 200 check
# Timeout settings for backends
timeout server 30s
timeout connect 5s
Create statistics interface
Enable HAProxy statistics page for monitoring rate limiting effectiveness and server health.
# Statistics interface
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
stats hide-version
stats realm HAProxy\ Statistics
stats auth admin:secure_password_here
# Security for stats interface
acl stats_admin src 127.0.0.1 10.0.0.0/8 192.168.0.0/16
http-request deny unless stats_admin
Create whitelist for trusted IPs
Create a whitelist file for trusted IP addresses that should bypass rate limiting.
sudo mkdir -p /etc/haproxy
sudo touch /etc/haproxy/whitelist_ips.txt
# Trusted IP addresses (monitoring systems, CDN, etc.)
127.0.0.1
10.0.0.0/8
192.168.0.0/16
Add your monitoring server IPs here
203.0.113.100
198.51.100.0/24
Configure logging for security monitoring
Set up dedicated logging for HAProxy to monitor rate limiting actions and security events.
$ModLoad imudp
$UDPServerRun 514
$UDPServerAddress 127.0.0.1
HAProxy log processing
local0.* /var/log/haproxy.log
& stop
Set up log rotation
Configure log rotation to prevent HAProxy security logs from consuming too much disk space.
/var/log/haproxy.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
postrotate
/bin/kill -HUP $(cat /var/run/rsyslogd.pid 2> /dev/null) 2> /dev/null || true
endscript
}
Test configuration and start services
Validate the HAProxy configuration and start the services with security monitoring enabled.
# Test HAProxy configuration
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
Restart services
sudo systemctl restart rsyslog
sudo systemctl enable haproxy
sudo systemctl restart haproxy
Check service status
sudo systemctl status haproxy
sudo systemctl status rsyslog
Create monitoring script
Set up a monitoring script to track rate limiting effectiveness and send alerts.
#!/bin/bash
HAProxy Security Monitor Script
LOG_FILE="/var/log/haproxy.log"
ALERT_EMAIL="admin@example.com"
THRESHOLD_BLOCKS=50
Count blocked requests in the last 5 minutes
BLOCKED_COUNT=$(tail -n 1000 $LOG_FILE | grep "$(date -d '5 minutes ago' '+%b %d %H:%M')" | grep -c "HTTP/1.1" | grep -c "403\|429\|444")
if [ $BLOCKED_COUNT -gt $THRESHOLD_BLOCKS ]; then
echo "High number of blocked requests detected: $BLOCKED_COUNT" | mail -s "HAProxy Security Alert" $ALERT_EMAIL
logger "HAProxy Security Alert: $BLOCKED_COUNT blocked requests in last 5 minutes"
fi
Check for potential DDoS
DDOS_IPS=$(tail -n 2000 $LOG_FILE | grep "$(date '+%b %d %H:%M')" | awk '{print $6}' | sort | uniq -c | awk '$1 > 100 {print $2}' | wc -l)
if [ $DDOS_IPS -gt 0 ]; then
echo "Potential DDoS detected from $DDOS_IPS IP addresses" | mail -s "HAProxy DDoS Alert" $ALERT_EMAIL
fi
sudo chmod +x /usr/local/bin/haproxy_security_monitor.sh
Add to crontab for regular monitoring
sudo crontab -e
Add this line:
/5 * /usr/local/bin/haproxy_security_monitor.sh
Configure advanced stick table sharing
Set up stick table synchronization
For multi-HAProxy deployments, configure stick table synchronization to share rate limiting data between instances.
# Add to the peers section for cluster synchronization
peers mycluster
peer haproxy1 192.168.1.10:1024
peer haproxy2 192.168.1.11:1024
Update frontend with peers configuration
frontend web_frontend
# ... previous configuration ...
stick-table type ip size 100k expire 30s store http_req_rate(10s),http_err_rate(10s),conn_rate(10s),sess_cnt peers mycluster
Configure firewall for HAProxy
Set up firewall rules to protect HAProxy while allowing legitimate traffic.
# Allow HTTP, HTTPS, and stats interface
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from 192.168.0.0/16 to any port 8404
sudo ufw allow from 10.0.0.0/8 to any port 8404
Verify your setup
Test that HAProxy is running with rate limiting and DDoS protection enabled.
# Check HAProxy status
sudo systemctl status haproxy
Verify configuration syntax
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
Test rate limiting with curl
for i in {1..25}; do curl -s -o /dev/null -w "%{http_code}\n" http://example.com/; done
Check statistics interface
curl -u admin:secure_password_here http://localhost:8404/stats
Monitor logs for rate limiting actions
sudo tail -f /var/log/haproxy.log | grep -E "403|429|tarpit"
Check stick table contents
echo "show table web_frontend" | sudo socat stdio /run/haproxy/admin.sock
Monitor and tune security rules
Set up Prometheus metrics collection
Enable HAProxy stats in Prometheus format for advanced monitoring and alerting.
# Add to the stats listen section
listen stats
# ... existing configuration ...
# Prometheus metrics endpoint
http-request use-service prometheus-exporter if { path /metrics }
stats show-modules
You can now monitor HAProxy performance and security metrics with monitoring solutions. Consider implementing HAProxy monitoring with Prometheus and Grafana for comprehensive observability.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Configuration test fails | Syntax error in haproxy.cfg | sudo haproxy -c -f /etc/haproxy/haproxy.cfg to identify specific errors |
| Rate limiting not working | Stick table not tracking correctly | Check track-sc0 src is properly configured in frontend |
| Legitimate users blocked | Rate limits too aggressive | Increase thresholds or add IPs to whitelist file |
| SSL certificate errors | Certificate path incorrect | Verify certificate file exists and HAProxy user has read access |
| Statistics page not accessible | Firewall blocking port 8404 | Configure firewall rules for stats interface access |
| Backend servers marked down | Health check failing | Verify /health endpoint exists on backend servers |
| High memory usage | Stick table size too large | Reduce stick table size or decrease expiration time |
| Logs not appearing | Rsyslog not configured | Restart rsyslog service and verify UDP listener on 127.0.0.1:514 |
Next steps
- Configure NGINX load balancing with health checks for additional reverse proxy options
- Monitor HAProxy and Consul with Prometheus and Grafana for comprehensive observability
- Set up HAProxy SSL termination with Let's Encrypt certificates
- Configure HAProxy with Consul service discovery for dynamic backend management
- Implement HAProxy WAF integration with ModSecurity for application-layer protection
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' # No Color
# Configuration
DOMAIN="${1:-example.com}"
BACKEND_IP="${2:-127.0.0.1:8080}"
# Usage function
usage() {
echo "Usage: $0 [domain] [backend_ip:port]"
echo " domain: Domain name for SSL (default: example.com)"
echo " backend_ip:port: Backend server (default: 127.0.0.1:8080)"
exit 1
}
# Error handling
cleanup() {
echo -e "${RED}[ERROR]${NC} Installation failed. Cleaning up..."
if [[ -f /etc/haproxy/haproxy.cfg.backup ]]; then
mv /etc/haproxy/haproxy.cfg.backup /etc/haproxy/haproxy.cfg 2>/dev/null || true
fi
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}[ERROR]${NC} This script must be run as root"
exit 1
fi
# 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 && apt upgrade -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
;;
*)
echo -e "${RED}[ERROR]${NC} Unsupported distribution: $ID"
exit 1
;;
esac
else
echo -e "${RED}[ERROR]${NC} Cannot detect distribution"
exit 1
fi
echo -e "${GREEN}[INFO]${NC} Installing HAProxy with DDoS protection on $PRETTY_NAME"
echo -e "${GREEN}[INFO]${NC} Domain: $DOMAIN, Backend: $BACKEND_IP"
# Step 1: Update system packages
echo -e "${GREEN}[1/8]${NC} Updating system packages..."
$PKG_UPDATE
# Step 2: Install HAProxy and dependencies
echo -e "${GREEN}[2/8]${NC} Installing HAProxy and dependencies..."
$PKG_INSTALL haproxy rsyslog openssl
# Step 3: Create backup of default configuration
echo -e "${GREEN}[3/8]${NC} Backing up default configuration..."
cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.backup
# Step 4: Create SSL directory and self-signed certificate
echo -e "${GREEN}[4/8]${NC} Creating SSL certificate..."
mkdir -p /etc/ssl/certs
if [[ ! -f "/etc/ssl/certs/${DOMAIN}.pem" ]]; then
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "/etc/ssl/private/${DOMAIN}.key" \
-out "/etc/ssl/certs/${DOMAIN}.crt" \
-subj "/C=US/ST=State/L=City/O=Organization/CN=${DOMAIN}" 2>/dev/null
cat "/etc/ssl/certs/${DOMAIN}.crt" "/etc/ssl/private/${DOMAIN}.key" > "/etc/ssl/certs/${DOMAIN}.pem"
chmod 600 "/etc/ssl/certs/${DOMAIN}.pem"
chown haproxy:haproxy "/etc/ssl/certs/${DOMAIN}.pem"
fi
# Step 5: Configure rsyslog for HAProxy
echo -e "${GREEN}[5/8]${NC} Configuring logging..."
cat > /etc/rsyslog.d/49-haproxy.conf << 'EOF'
$ModLoad imudp
$UDPServerRun 514
$UDPServerAddress 127.0.0.1
local0.* /var/log/haproxy.log
& stop
EOF
# Step 6: Create HAProxy configuration
echo -e "${GREEN}[6/8]${NC} Creating HAProxy configuration..."
cat > /etc/haproxy/haproxy.cfg << EOF
global
log 127.0.0.1:514 local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
# Security settings
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
# Performance tuning
tune.ssl.default-dh-param 2048
tune.bufsize 32768
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
timeout http-request 15s
timeout http-keep-alive 15s
# Security headers
option httplog
option dontlognull
option log-health-checks
option forwardfor
option http-server-close
# Error handling
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
# Rate limiting frontend
frontend web_frontend
bind *:80
bind *:443 ssl crt /etc/ssl/certs/${DOMAIN}.pem
# Redirect HTTP to HTTPS
redirect scheme https if !{ ssl_fc }
# Stick tables for tracking
stick-table type ip size 100k expire 30s store http_req_rate(10s),http_err_rate(10s),conn_rate(10s),sess_cnt
# Track requests per IP
http-request track-sc0 src
# DDoS protection rules
# Block if more than 20 requests per 10 seconds
http-request deny if { sc_http_req_rate(0) gt 20 }
# Block if more than 10 connections per 10 seconds
http-request deny if { sc_conn_rate(0) gt 10 }
# Block if error rate is too high (more than 10 errors in 10s)
http-request deny if { sc_http_err_rate(0) gt 10 }
# Tarpit suspicious traffic (slow response for abusers)
http-request tarpit if { sc_http_req_rate(0) gt 15 } { sc_http_req_rate(0) lt 21 }
# Security headers
http-response set-header X-Frame-Options SAMEORIGIN
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-XSS-Protection "1; mode=block"
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
default_backend web_servers
# Backend configuration
backend web_servers
balance roundrobin
option httpchk GET /
http-check expect status 200
# Backend server
server web1 ${BACKEND_IP} check
# Stats page
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats admin if TRUE
EOF
# Set proper permissions
chmod 644 /etc/haproxy/haproxy.cfg
chown root:haproxy /etc/haproxy/haproxy.cfg
# Step 7: Configure firewall
echo -e "${GREEN}[7/8]${NC} Configuring firewall..."
if command -v ufw >/dev/null 2>&1; then
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 8404/tcp
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-port=8404/tcp
firewall-cmd --reload
fi
# Step 8: Start and enable services
echo -e "${GREEN}[8/8]${NC} Starting services..."
systemctl restart rsyslog
systemctl enable haproxy
systemctl restart haproxy
# Verification
echo -e "${GREEN}[VERIFY]${NC} Running verification checks..."
sleep 2
if systemctl is-active --quiet haproxy; then
echo -e "${GREEN}✓${NC} HAProxy is running"
else
echo -e "${RED}✗${NC} HAProxy failed to start"
exit 1
fi
if netstat -tlnp | grep -q ":80.*haproxy"; then
echo -e "${GREEN}✓${NC} HTTP port (80) is listening"
else
echo -e "${YELLOW}!${NC} HTTP port (80) not detected"
fi
if netstat -tlnp | grep -q ":443.*haproxy"; then
echo -e "${GREEN}✓${NC} HTTPS port (443) is listening"
else
echo -e "${YELLOW}!${NC} HTTPS port (443) not detected"
fi
echo -e "${GREEN}[SUCCESS]${NC} HAProxy with DDoS protection installed successfully!"
echo -e "${GREEN}[INFO]${NC} Configuration file: /etc/haproxy/haproxy.cfg"
echo -e "${GREEN}[INFO]${NC} Stats page: http://localhost:8404/stats"
echo -e "${GREEN}[INFO]${NC} Logs: /var/log/haproxy.log"
echo -e "${YELLOW}[NOTE]${NC} Update backend server IP in config if needed: $BACKEND_IP"
trap - ERR
Review the script before running. Execute with: bash install.sh