High-performance TCP/HTTP load balancer and reverse proxy for distributing traffic across backend servers.

Addresses below are RFC 5737 documentation ranges or placeholders - swap in your own.

Table of Contents#

  1. Overview
  2. Installation and Setup
  3. Configuration Structure
  4. HTTP Configuration
  5. SSL/TLS Termination
  6. ACLs and Routing Rules
  7. Rate Limiting
  8. Health Checks
  9. Stats and Monitoring
  10. Database Load Balancing Examples
  11. Performance Tuning
  12. Troubleshooting
  13. See Also
  14. Sources

1. Overview#

HAProxy (High Availability Proxy) is an open-source load balancer and reverse proxy for TCP and HTTP applications. It handles tens to hundreds of thousands of concurrent connections with minimal resource consumption. HAProxy operates in either TCP (Layer 4) or HTTP (Layer 7) mode, providing flexible traffic distribution, health checking, SSL termination, and detailed statistics. It is the de facto standard for database cluster load balancing (Patroni, Galera) and high-traffic web applications.

Key capabilities:

  • Layer 4 (TCP) and Layer 7 (HTTP) load balancing
  • SSL/TLS termination and passthrough
  • Content-based routing with ACLs
  • Stick tables for rate limiting and session persistence
  • HTTP/2 and HTTP/3 (QUIC) support (3.0+)
  • Built-in stats dashboard and socket API

2. Installation and Setup#

2.1 Package Installation#

RHEL/CentOS/Fedora:

dnf install -y haproxy

Debian/Ubuntu:

apt install -y haproxy

Arch Linux:

pacman -S haproxy

2.2 Enable and Start#

systemctl enable --now haproxy

2.3 SELinux Configuration (RHEL-based)#

When running on SELinux-enforcing systems, set the required booleans:

setsebool -P httpd_can_network_connect_db 1
setsebool -P httpd_can_network_connect 1
setsebool -P haproxy_connect_any 1
setsebool -P nis_enabled 1

2.4 Enable Multi-File Configuration#

Split configuration into multiple files for maintainability:

mkdir -p /etc/haproxy/haproxy.d
sed -i 's/OPTIONS=""/OPTIONS="-f \/etc\/haproxy\/haproxy.d"/' /etc/sysconfig/haproxy

On Debian-based systems, edit /etc/default/haproxy instead:

mkdir -p /etc/haproxy/conf.d
echo 'EXTRAOPTS="-f /etc/haproxy/conf.d"' >> /etc/default/haproxy

2.5 Validate Configuration#

Always validate before reloading:

haproxy -c -f /etc/haproxy/haproxy.cfg
systemctl reload haproxy

3. Configuration Structure#

HAProxy configuration is divided into four major sections: global, defaults, frontend, and backend. An optional listen section combines frontend and backend into one block.

3.1 Global Section#

Process-wide settings such as logging, resource limits, and security:

global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     8192
    user        haproxy
    group       haproxy
    daemon

    # Performance
    nbthread    4
    cpu-map     auto:1/1-4 0-3

    # Stats socket for runtime API
    stats socket /var/run/haproxy.sock mode 600 level admin
    stats timeout 30s

3.2 Defaults Section#

Default values inherited by all frontend and backend sections:

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option                  http-server-close
    option                  forwardfor except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

Key timeout parameters:

TimeoutPurposeRecommended
http-requestTime to receive complete HTTP request5-10s
connectTime to establish connection to backend5-10s
clientInactivity timeout on client side30s-5m
serverInactivity timeout on server side30s-5m
http-keep-aliveIdle time for keep-alive connections10s
checkHealth check timeout5-10s
queueTime a request can wait in queue30s-1m

4. HTTP Configuration#

4.1 Basic HTTP Frontend and Backend#

frontend http_front
    bind *:80
    default_backend http_back

backend http_back
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    server web1 192.0.2.11:8080 check
    server web2 192.0.2.12:8080 check
    server web3 192.0.2.13:8080 check backup

4.2 Listen Block (Combined Frontend/Backend)#

listen webapp
    bind *:80
    balance leastconn
    option httpchk GET /health
    server web1 192.0.2.11:8080 check weight 3
    server web2 192.0.2.12:8080 check weight 2
    server web3 192.0.2.13:8080 check weight 1

4.3 HTTP Headers and Forwarding#

frontend http_front
    bind *:80

    # Add headers for backend awareness
    http-request set-header X-Forwarded-Proto http
    http-request set-header X-Real-IP %[src]

    # Remove server-identifying headers
    http-response del-header Server
    http-response del-header X-Powered-By

    default_backend http_back

4.4 Load Balancing Algorithms#

# Round Robin (default) - equal distribution
balance roundrobin

# Least Connections - fewest active connections
balance leastconn

# Source IP Hash - session persistence by client IP
balance source

# URI Hash - same URI goes to same server (caching)
balance uri

# First available server with capacity
balance first

# Random with weight
balance random

4.5 HTTP/2 Support#

frontend https_front
    bind *:443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
    default_backend http_back

5. SSL/TLS Termination#

5.1 Basic SSL Termination#

HAProxy terminates TLS and forwards plaintext HTTP to backends:

frontend https_front
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem
    bind *:80
    http-request redirect scheme https unless { ssl_fc }
    default_backend http_back

backend http_back
    server web1 192.0.2.11:8080 check

The PEM file must contain the certificate, private key, and any intermediate certificates concatenated:

cat cert.pem intermediate.pem privkey.pem > /etc/haproxy/certs/example.com.pem

5.2 SSL Hardening#

global
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
    tune.ssl.default-dh-param 2048

5.3 SSL Passthrough#

Forward encrypted traffic to backends without terminating TLS:

frontend ssl_passthrough
    bind *:443
    mode tcp
    option tcplog
    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 1 }

    use_backend app1_back if { req_ssl_sni -i app1.example.com }
    use_backend app2_back if { req_ssl_sni -i app2.example.com }

backend app1_back
    mode tcp
    server app1 192.0.2.11:443 check

5.4 Backend SSL (Re-encryption)#

backend secure_back
    server web1 192.0.2.11:443 ssl verify required ca-file /etc/haproxy/ca.pem check

6. ACLs and Routing Rules#

ACLs (Access Control Lists) match conditions in requests for content-based routing.

6.1 ACL Syntax#

acl <name> <criterion> [flags] <value>

6.2 Host-Based Routing#

frontend http_front
    bind *:80

    acl is_app1 hdr(host) -i app1.example.com
    acl is_app2 hdr(host) -i app2.example.com
    acl is_api  hdr(host) -i api.example.com

    use_backend app1_back if is_app1
    use_backend app2_back if is_app2
    use_backend api_back  if is_api
    default_backend fallback_back

6.3 Path-Based Routing#

frontend http_front
    bind *:80

    acl is_api     path_beg /api/
    acl is_static  path_end .css .js .png .jpg .gif .ico
    acl is_ws      path_beg /ws/

    use_backend api_back    if is_api
    use_backend static_back if is_static
    use_backend ws_back     if is_ws
    default_backend app_back

6.4 IP-Based Access Control#

frontend http_front
    bind *:80

    acl allowed_ips src 192.0.2.0/24 198.51.100.0/24
    acl is_admin path_beg /admin

    http-request deny if is_admin !allowed_ips

    default_backend app_back

6.5 Method-Based Routing#

acl is_get     method GET
acl is_post    method POST
acl is_options method OPTIONS

# CORS preflight
http-request return status 204 if is_options

7. Rate Limiting#

HAProxy uses stick tables to track client behavior and enforce rate limits.

7.1 Connection Rate Limiting#

frontend http_front
    bind *:80

    # Track requests per IP over 10 seconds
    stick-table type ip size 100k expire 30s store http_req_rate(10s)
    http-request track-sc0 src

    # Deny if more than 100 requests in 10 seconds
    acl rate_limited sc_http_req_rate(0) gt 100
    http-request deny deny_status 429 if rate_limited

    default_backend app_back

7.2 Concurrent Connection Limiting#

frontend http_front
    bind *:80

    stick-table type ip size 100k expire 30s store conn_cur
    http-request track-sc0 src

    # Deny if more than 50 concurrent connections per IP
    acl too_many_conns sc_conn_cur(0) gt 50
    http-request deny deny_status 429 if too_many_conns

    default_backend app_back

7.3 Tarpit Abusive Clients#

Slow down abusive clients instead of rejecting immediately:

    acl rate_abusive sc_http_req_rate(0) gt 200
    http-request tarpit if rate_abusive
    timeout tarpit 5s

8. Health Checks#

8.1 HTTP Health Check#

backend app_back
    option httpchk GET /health
    http-check expect status 200
    default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
    server web1 192.0.2.11:8080 maxconn 100 check
    server web2 192.0.2.12:8080 maxconn 100 check

8.2 TCP Health Check#

backend tcp_back
    mode tcp
    option tcp-check
    tcp-check connect
    tcp-check send PING\r\n
    tcp-check expect string +PONG
    server redis1 192.0.2.11:6379 check

8.3 Health Check Parameters#

ParameterPurpose
inter <time>Interval between checks (default 2s)
fall <count>Consecutive failures before marking down
rise <count>Consecutive successes before marking up
on-marked-down shutdown-sessionsKill active sessions when server goes down
slowstart <time>Gradually increase traffic to recovered server

9. Stats and Monitoring#

9.1 Stats Web Dashboard#

listen stats
    bind *:8404
    mode http
    stats enable
    stats uri /stats
    stats refresh 10s
    stats show-legends
    stats auth admin:<password>
    stats admin if TRUE

Access at http://<haproxy-ip>:8404/stats.

9.2 Stats Socket (Runtime API)#

Configure in the global section:

global
    stats socket /var/run/haproxy.sock mode 600 level admin
    stats timeout 30s

Common runtime commands:

# Show server status
echo "show stat" | socat stdio /var/run/haproxy.sock

# Disable a backend server
echo "disable server app_back/web1" | socat stdio /var/run/haproxy.sock

# Enable a backend server
echo "enable server app_back/web1" | socat stdio /var/run/haproxy.sock

# Show session info
echo "show sess" | socat stdio /var/run/haproxy.sock

# Show current connections
echo "show info" | socat stdio /var/run/haproxy.sock

# Set server weight dynamically
echo "set weight app_back/web1 50%" | socat stdio /var/run/haproxy.sock

9.3 Prometheus Integration#

frontend prometheus
    bind *:8405
    mode http
    http-request use-service prometheus-exporter if { path /metrics }
    no log

10. Database Load Balancing Examples#

10.1 Patroni (PostgreSQL HA)#

Route traffic to the Patroni leader using HTTP health checks against the Patroni REST API:

frontend patroni0_frontend
    bind *:5432
    mode tcp
    maxconn 100
    timeout client 3600s
    default_backend patroni0_backend

backend patroni0_backend
    mode tcp
    option httpchk GET /primary
    timeout check 5s
    timeout connect 2s
    timeout server 3600s
    http-check expect status 200
    default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
    server node01 192.0.2.111:6432 maxconn 100 check port 8008
    server node02 192.0.2.112:6432 maxconn 100 check port 8008

# Read replicas (optional)
frontend patroni0_ro_frontend
    bind *:5433
    mode tcp
    maxconn 200
    timeout client 3600s
    default_backend patroni0_ro_backend

backend patroni0_ro_backend
    mode tcp
    option httpchk GET /replica
    timeout check 5s
    timeout connect 2s
    timeout server 3600s
    http-check expect status 200
    balance roundrobin
    default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions
    server node01 192.0.2.111:6432 maxconn 100 check port 8008
    server node02 192.0.2.112:6432 maxconn 100 check port 8008

10.2 Galera (MySQL/MariaDB HA)#

frontend galera_frontend
    bind *:3306
    mode tcp
    default_backend galera_backend

backend galera_backend
    mode tcp
    balance roundrobin
    option mysql-check user haproxy
    server node01 192.0.2.111:3306 check weight 1
    server node02 192.0.2.112:3306 check weight 1
    server node03 192.0.2.113:3306 check weight 1 backup

11. Performance Tuning#

11.1 Global Tuning#

global
    maxconn         50000
    nbthread        4
    cpu-map         auto:1/1-4 0-3

    tune.bufsize            32768
    tune.http.maxhdr        128
    tune.ssl.default-dh-param 2048
    tune.ssl.cachesize      100000
    tune.ssl.lifetime       300
    tune.comp.maxlevel      5

11.2 System-Level Tuning#

Increase system limits for high-connection environments:

# /etc/security/limits.d/haproxy.conf
haproxy  soft  nofile  100000
haproxy  hard  nofile  100000
# /etc/sysctl.d/haproxy.conf
net.ipv4.tcp_max_syn_backlog = 65535
net.core.somaxconn = 65535
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1

11.3 Connection Reuse#

defaults
    option http-keep-alive
    http-reuse safe

11.4 Compression#

defaults
    compression algo gzip
    compression type text/html text/plain text/css application/javascript application/json

12. Troubleshooting#

IssueCauseSolution
Starting proxy: cannot bind socketPort already in use or insufficient permissionsCheck with ss -tlnp; ensure HAProxy user can bind to the port
Server marked DOWNBackend health check failingVerify backend is reachable; check health check endpoint and port
503 Service UnavailableAll backend servers are downCheck backend health; review show stat via stats socket
408 Request TimeoutClient did not send complete request in timeIncrease timeout http-request
SELinux avc: deniedSELinux blocking HAProxy connectionsSet haproxy_connect_any boolean; check audit2allow
High memory usageToo many stick table entries or large buffersReduce stick-table size; lower tune.bufsize
SSL handshake failuresMismatched ciphers or expired certificatesVerify PEM file order; check ssl-default-bind-options
Connection refused to statsStats listener not configured or wrong portVerify listen stats block and bind address
Configuration not loadingSyntax error in config fileRun haproxy -c -f /etc/haproxy/haproxy.cfg to validate

See Also#

Sources#