DEV Community

Naveen Karasu
Naveen Karasu

Posted on

Day 17/60: HAProxy Fundamentals -- Production Security Engineering

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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)