Day 17/60: HAProxy Fundamentals -- Production Security Engineering
60 Day Production Security Engineering Challenge
HAProxy is the load balancer I have deployed most often in production environments. Today I am walking through the configuration patterns that matter for reliability and security.
Frontend and Backend Sections
HAProxy separates concerns into frontends (where traffic enters) and backends (where it goes). A frontend binds a port and applies routing logic. A backend defines the server pool and health check strategy.
frontend http_in
bind *:80
bind *:443 ssl crt /etc/haproxy/certs/app.pem
http-request redirect scheme https unless { ssl_fc }
acl is_api path_beg /api/
acl is_static path_beg /assets/ /images/ /css/ /js/
use_backend api_pool if is_api
use_backend cdn_origin if is_static
default_backend web_pool
backend web_pool
balance leastconn
option httpchk GET /healthz HTTP/1.1\r\nHost:\ localhost
http-check expect status 200
server web1 10.0.2.10:3000 check inter 5s fall 3 rise 2
server web2 10.0.2.11:3000 check inter 5s fall 3 rise 2
backend api_pool
balance roundrobin
option httpchk GET /api/ping
server api1 10.0.3.10:8080 check inter 3s fall 2 rise 3
server api2 10.0.3.11:8080 check inter 3s fall 2 rise 3
server api3 10.0.3.12:8080 check inter 3s fall 2 rise 3
The leastconn balance algorithm routes to the server with the fewest active connections. Good for endpoints with variable response times. roundrobin distributes evenly and works well when request cost is uniform.
Stick Tables for Rate Limiting
Stick tables let HAProxy track per-IP connection metadata in memory and enforce limits at the load balancer layer:
frontend login_protection
bind *:443 ssl crt /etc/haproxy/certs/login.pem
stick-table type ip size 50k expire 120s store http_req_rate(30s),conn_cur,gpc0
http-request track-sc0 src
acl too_fast sc0_http_req_rate gt 30
acl too_many_conn sc0_conn_cur gt 10
http-request deny deny_status 429 if too_fast
http-request deny deny_status 429 if too_many_conn
http-request set-header X-Rate-Remaining %[sc0_http_req_rate,sub(30)]
default_backend login_servers
Named ACLs make the intent readable. The gpc0 general purpose counter lets you flag IPs from external threat feeds via the runtime API.
Runtime Socket API
The socket API manages HAProxy at runtime. Here is a deploy script pattern I use in production:
#!/bin/bash
SOCKET="/var/run/haproxy.sock"
BACKEND="login_servers"
SERVER="login1"
# Pull server from rotation
echo "set server $BACKEND/$SERVER state maint" | socat stdio $SOCKET
echo "Waiting for connections to drain..."
sleep 15
# Verify no active sessions
SESSIONS=$(echo "show servers conn $BACKEND" | socat stdio $SOCKET | grep $SERVER)
echo "Current state: $SESSIONS"
# Deploy and restart app
systemctl restart myapp
# Return to rotation
echo "set server $BACKEND/$SERVER state ready" | socat stdio $SOCKET
echo "set server $BACKEND/$SERVER weight 256" | socat stdio $SOCKET
echo "Deploy complete for $SERVER"
Key Takeaways
HAProxy's frontend/backend separation, native rate limiting through stick tables, and runtime API for zero-downtime deploys make it operationally excellent. These features are why it remains the go-to load balancer for high-traffic production environments.
Day 17/60 of the 60 Day Production Security Engineering Challenge
Top comments (0)