How to Configure HAProxy for HTTP and TCP Load Balancing on RHEL 7

HAProxy is one of the most widely deployed open-source load balancers in production infrastructure, renowned for its high throughput, low latency, and fine-grained configurability. On RHEL 7 it is available directly from the base repositories and integrates cleanly with systemctl and firewalld. Whether you need to distribute HTTP traffic across a cluster of web servers, balance raw TCP connections to a database tier, terminate TLS at the edge, or expose a real-time stats dashboard, HAProxy handles all of these use cases through a single unified configuration file. This guide walks you through installation, the structure of haproxy.cfg, ACL-based routing, balance algorithms, health checks, the stats page, SSL termination, and logging.

Prerequisites

  • RHEL 7 with root or sudo access
  • At least two backend servers (real or virtual) to load balance — referred to as web1 and web2 in examples
  • Firewalld running (or iptables if you prefer)
  • For SSL termination: a PEM-format certificate file combining the certificate, chain, and private key
  • Basic understanding of TCP/IP and HTTP

Step 1: Install HAProxy with yum

sudo yum install -y haproxy
haproxy -v
# HA-Proxy version 1.5.18 ...

RHEL 7 ships HAProxy 1.5. If you need 2.x features (multithreading, stick-tables with expire keyword improvements), enable RHSCL or install from a third-party repository such as IUS. For most production HTTP and TCP balancing workloads, 1.5 is fully capable.

Enable and start the service:

sudo systemctl enable haproxy
sudo systemctl start haproxy
sudo systemctl status haproxy

Step 2: Understanding the haproxy.cfg Structure

The configuration file at /etc/haproxy/haproxy.cfg is divided into four sections:

  • global — process-level settings: user, group, log sockets, max connections, SSL defaults
  • defaults — default values inherited by all frontend and backend sections unless overridden
  • frontend — a listening socket that accepts client connections and dispatches them to backends
  • backend — a pool of servers that receive forwarded connections

A listen section is shorthand that merges a frontend and backend into one block, useful for simple TCP proxying.

Step 3: Write a Complete HTTP Load-Balancing Configuration

Back up the default file and write a fresh configuration:

sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
sudo vi /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    log         /dev/log local0 info
    log         /dev/log local1 notice
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     50000
    user        haproxy
    group       haproxy
    daemon

    # SSL/TLS defaults
    ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
    tune.ssl.default-dh-param 2048

#---------------------------------------------------------------------
# Default settings inherited by all frontend/backend sections
#---------------------------------------------------------------------
defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option                  forwardfor
    option                  http-server-close
    option                  redispatch
    retries                 3
    timeout connect         5s
    timeout client          50s
    timeout server          50s
    timeout http-request    15s
    timeout http-keep-alive 15s
    timeout queue           30s
    timeout tunnel          3600s
    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

#---------------------------------------------------------------------
# HTTP frontend — listens on port 80, routes via ACLs
#---------------------------------------------------------------------
frontend http_frontend
    bind *:80
    mode http

    # Capture the Host header for logging
    http-request capture req.hdr(Host) len 64

    # ACL definitions
    acl is_api      path_beg /api/
    acl is_static   path_end .css .js .png .jpg .jpeg .gif .ico .woff2 .svg
    acl is_www      hdr(host) -i www.example.com example.com

    # Route API requests to a dedicated API backend
    use_backend api_servers   if is_api
    # Route static assets to the static backend
    use_backend static_servers if is_static
    # Default: send to the main web backend
    default_backend web_servers

#---------------------------------------------------------------------
# Main web backend — round-robin with health checks
#---------------------------------------------------------------------
backend web_servers
    mode http
    balance roundrobin
    option httpchk GET /health HTTP/1.1rnHost: example.com

    server web1 192.168.1.10:80 check inter 5s rise 2 fall 3 weight 1
    server web2 192.168.1.11:80 check inter 5s rise 2 fall 3 weight 1
    server web3 192.168.1.12:80 check inter 5s rise 2 fall 3 weight 2 backup

#---------------------------------------------------------------------
# API backend — least-connections for long-lived requests
#---------------------------------------------------------------------
backend api_servers
    mode http
    balance leastconn
    option httpchk GET /api/health HTTP/1.1rnHost: api.example.com
    http-request set-header X-Forwarded-Proto http

    server api1 192.168.1.20:8080 check inter 3s rise 2 fall 2
    server api2 192.168.1.21:8080 check inter 3s rise 2 fall 2

#---------------------------------------------------------------------
# Static asset backend — source (IP-based sticky) for CDN edge nodes
#---------------------------------------------------------------------
backend static_servers
    mode http
    balance source
    option httpchk HEAD /favicon.ico HTTP/1.1rnHost: static.example.com

    server static1 192.168.1.30:80 check
    server static2 192.168.1.31:80 check

Step 4: Balance Algorithms Explained

  • roundrobin — distributes requests evenly in turn; respects server weights; best general-purpose choice
  • leastconn — sends each new request to the server with the fewest active connections; ideal for long-lived connections (WebSockets, database proxying)
  • source — hashes the client IP to always route a given client to the same server (sticky sessions without cookies)
  • uri — hashes the request URI; useful for caching tiers so the same URL always hits the same cache node
  • first — fills servers sequentially; the second server only receives traffic when the first is at max connections

Step 5: Health Checks

The check keyword on each server line enables health checking. The options used above:

  • inter 5s — check interval
  • rise 2 — number of consecutive successful checks before marking a server UP
  • fall 3 — number of consecutive failed checks before marking it DOWN
  • weight 2 — server receives twice as many requests as weight-1 servers
  • backup — only used when all non-backup servers are down

For HTTP health checks, option httpchk sends a real HTTP request instead of just testing the TCP connection, which catches application-level failures:

option httpchk GET /health HTTP/1.1rnHost: example.com
http-check expect status 200

Step 6: Enable the Stats Page

HAProxy includes a built-in statistics dashboard. Add a dedicated frontend or use a listen block:

listen stats
    bind *:9000
    mode http
    stats enable
    stats uri /haproxy-stats
    stats realm "HAProxy Statistics"
    stats auth admin:SecurePassword123
    stats refresh 5s
    stats show-legends
    stats show-node
    # Restrict access to management networks only
    acl mgmt src 192.168.1.0/24 10.0.0.0/8
    http-request deny if !mgmt

Open port 9000 in firewalld:

sudo firewall-cmd --permanent --add-port=9000/tcp
sudo firewall-cmd --reload

Browse to http://<server-ip>:9000/haproxy-stats to see real-time server health, connection counts, and data rates.

Step 7: SSL Termination

Create a combined PEM file from your certificate, chain, and private key:

cat /etc/ssl/certs/example.com.crt 
    /etc/ssl/certs/example.com-chain.crt 
    /etc/ssl/private/example.com.key 
    > /etc/haproxy/certs/example.com.pem

chmod 600 /etc/haproxy/certs/example.com.pem
chown haproxy:haproxy /etc/haproxy/certs/example.com.pem

Add an HTTPS frontend to your configuration:

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
    mode http
    http-request set-header X-Forwarded-Proto https
    http-request set-header X-Forwarded-Port 443

    # Redirect HTTP to HTTPS
    redirect scheme https if !{ ssl_fc }

    default_backend web_servers

frontend http_redirect
    bind *:80
    mode http
    redirect scheme https code 301

Step 8: TCP Load Balancing

For raw TCP proxying (MySQL, PostgreSQL, Redis), use mode tcp and a listen block:

listen mysql_cluster
    bind *:3306
    mode tcp
    balance leastconn
    option tcp-check
    timeout connect 3s
    timeout client  1m
    timeout server  1m

    server db1 192.168.1.40:3306 check
    server db2 192.168.1.41:3306 check

Step 9: Configure rsyslog Logging

HAProxy logs to syslog. On RHEL 7 with rsyslog, create a dedicated configuration:

sudo vi /etc/rsyslog.d/haproxy.conf
$ModLoad imudp
$UDPServerRun 514
$UDPServerAddress 127.0.0.1

local0.* /var/log/haproxy.log
local1.* /var/log/haproxy-notice.log
sudo systemctl restart rsyslog

Step 10: Validate and Reload

# Syntax check
sudo haproxy -c -f /etc/haproxy/haproxy.cfg

# Reload without dropping active connections
sudo systemctl reload haproxy

# Tail the log
sudo tail -f /var/log/haproxy.log

Conclusion

HAProxy on RHEL 7 delivers enterprise-grade load balancing through a straightforward configuration language. The global/defaults/frontend/backend structure keeps your configuration modular and readable as it grows. ACL-based routing lets you direct different request types — API calls, static assets, application pages — to purpose-built backend pools, each with its own health-check strategy and balance algorithm. The built-in stats page provides instant visibility into backend health without external tooling, and the systemd reload mechanism ensures configuration changes take effect with zero connection drops. For production deployments, combine HAProxy with keepalived on two nodes for a highly available, failover-capable load balancing layer.