HAProxy SSL/TLS Configuration

Best practices for SSL/TLS termination in HAProxy including load balancing, modern security standards, and performance optimization.

Basic SSL Termination

Basic HTTPS frontend

Minimal SSL termination configuration:

global
    log /dev/log local0
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

    # SSL settings
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem
    default_backend web_servers

backend web_servers
    balance roundrobin
    server web1 192.168.1.10:80 check
    server web2 192.168.1.11:80 check

Combined certificate file

HAProxy requires certificate and key in single PEM file:

# Combine certificate, key, and chain
cat example.com.crt example.com.key intermediate.crt > /etc/haproxy/certs/example.com.pem

# Set proper permissions
chmod 600 /etc/haproxy/certs/example.com.pem
chown haproxy:haproxy /etc/haproxy/certs/example.com.pem

HTTP to HTTPS redirect

Force all traffic to HTTPS:

frontend http_frontend
    bind *:80
    redirect scheme https code 301 if !{ ssl_fc }

Modern Security Configuration

TLS protocols and ciphers

Recommended SSL/TLS configuration for 2024+:

global
    # Modern configuration (TLS 1.2 and 1.3 only)
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    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

    # Server-side SSL configuration
    ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets

DH parameters

Generate and use strong Diffie-Hellman parameters:

# Generate DH params (one-time setup)
openssl dhparam -out /etc/haproxy/dhparams.pem 4096

# Add to global section
global
    ssl-dh-param-file /etc/haproxy/dhparams.pem

Curve preferences

Specify elliptic curve preferences:

global
    # HAProxy 2.4+
    ssl-default-bind-curves X25519:secp384r1:secp521r1

HSTS and Security Headers

Complete security headers

Add security headers to HTTPS frontend:

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/

    # HSTS with 2-year max-age
    http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

    # Additional security headers
    http-response set-header X-Content-Type-Options "nosniff"
    http-response set-header X-Frame-Options "SAMEORIGIN"
    http-response set-header X-XSS-Protection "1; mode=block"
    http-response set-header Referrer-Policy "strict-origin-when-cross-origin"

    # Content Security Policy
    http-response set-header Content-Security-Policy "default-src 'self' https:"

    default_backend web_servers

Remove server headers

Hide server version information:

frontend https_frontend
    # Remove server identification
    http-response del-header Server
    http-response set-header Server "Secure Web Server"

    # Remove X-Powered-By
    http-response del-header X-Powered-By

OCSP Stapling

Enable OCSP stapling

HAProxy 2.2+ supports OCSP stapling:

# Fetch OCSP response manually
openssl ocsp -issuer intermediate.crt -cert example.com.crt \
  -url http://ocsp.example.com -respout example.com.ocsp

# Add OCSP response to bind line
frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem ocsp-update on

Automatic OCSP updates

Configure automatic OCSP response updates (HAProxy 2.2+):

global
    # Enable OCSP auto-update
    ocsp-update.mode on
    ocsp-update.maxdelay 3600
    ocsp-update.mindelay 300

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/ ocsp-update on

Multi-Certificate SNI Configuration

Directory-based certificates

Load all certificates from directory (recommended):

# Place all combined PEM files in /etc/haproxy/certs/
# HAProxy automatically uses SNI to select correct cert

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/

    # HAProxy selects certificate based on SNI
    # example.com.pem for example.com
    # api.example.com.pem for api.example.com
    default_backend web_servers

Explicit certificate binding

Specify multiple certificates explicitly:

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem crt /etc/haproxy/certs/api.example.com.pem
    default_backend web_servers

SNI-based routing

Route to different backends based on SNI:

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/

    # Route based on hostname
    use_backend api_backend if { ssl_fc_sni api.example.com }
    use_backend web_backend if { ssl_fc_sni www.example.com }
    default_backend web_backend

backend api_backend
    balance roundrobin
    server api1 192.168.1.20:8080 check

backend web_backend
    balance roundrobin
    server web1 192.168.1.10:80 check
    server web2 192.168.1.11:80 check

Client Certificate Authentication (mTLS)

Require client certificates

Enable mutual TLS authentication:

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/ ca-file /etc/haproxy/ca/client-ca.pem verify required

    # Pass client cert info to backend
    http-request set-header X-SSL-Client-CN %{+Q}[ssl_c_s_dn(cn)]
    http-request set-header X-SSL-Client-DN %{+Q}[ssl_c_s_dn]
    http-request set-header X-SSL-Client-Verify %[ssl_c_verify]

    default_backend web_servers

Optional client certificates

Make client certs optional, check in backend:

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/ ca-file /etc/haproxy/ca/client-ca.pem verify optional

    # Only process if client cert is valid
    acl client_cert_valid ssl_c_verify 0
    http-request set-header X-SSL-Client-Verified true if client_cert_valid

    default_backend web_servers

CRL checking

Validate client certificates against CRL:

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/ ca-file /etc/haproxy/ca/client-ca.pem crl-file /etc/haproxy/ca/crl.pem verify required

SSL Passthrough (No Termination)

TCP mode passthrough

Forward encrypted traffic to backend without decryption:

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

    # Route based on SNI
    use_backend backend_example if { req_ssl_sni -i example.com }
    use_backend backend_api if { req_ssl_sni -i api.example.com }

backend backend_example
    mode tcp
    balance roundrobin
    server web1 192.168.1.10:443 check
    server web2 192.168.1.11:443 check

backend backend_api
    mode tcp
    balance roundrobin
    server api1 192.168.1.20:443 check

Complete Production Configuration

Hardened HAProxy SSL config

Complete example with all best practices:

global
    log /dev/log local0
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

    # SSL/TLS 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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
    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
    ssl-dh-param-file /etc/haproxy/dhparams.pem

    # OCSP
    ocsp-update.mode on
    ocsp-update.maxdelay 3600

    # Tuning
    tune.ssl.default-dh-param 4096
    maxconn 4096

defaults
    log global
    mode http
    option httplog
    option dontlognull
    option http-server-close
    option forwardfor except 127.0.0.0/8
    option redispatch
    retries 3
    timeout connect 5000
    timeout client  50000
    timeout server  50000

# HTTP redirect to HTTPS
frontend http_frontend
    bind *:80
    redirect scheme https code 301 if !{ ssl_fc }

# HTTPS frontend
frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 ocsp-update on

    # Security Headers
    http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    http-response set-header X-Content-Type-Options "nosniff"
    http-response set-header X-Frame-Options "SAMEORIGIN"
    http-response set-header X-XSS-Protection "1; mode=block"
    http-response set-header Referrer-Policy "strict-origin-when-cross-origin"

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

    # Logging
    capture request header Host len 64
    capture request header User-Agent len 128

    # Routing
    use_backend api_backend if { path_beg /api }
    default_backend web_backend

backend web_backend
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    server web1 192.168.1.10:80 check
    server web2 192.168.1.11:80 check

backend api_backend
    balance roundrobin
    option httpchk GET /api/health
    http-check expect status 200
    server api1 192.168.1.20:8080 check
    server api2 192.168.1.21:8080 check

# Stats interface
listen stats
    bind *:8404 ssl crt /etc/haproxy/certs/stats.pem
    stats enable
    stats uri /stats
    stats refresh 30s
    stats auth admin:yourpassword

Performance Optimization

HTTP/2 and ALPN

Enable HTTP/2 with ALPN negotiation:

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

SSL session cache

Tune SSL session caching:

global
    tune.ssl.cachesize 100000
    tune.ssl.lifetime 600
    tune.ssl.maxrecord 1419  # Optimized for MTU

Connection limits

Optimize connection handling:

global
    maxconn 4096
    maxsslconn 4096
    tune.ssl.default-dh-param 2048

Testing and Validation

Test configuration

Validate HAProxy config before reload:

# Test configuration syntax
haproxy -c -f /etc/haproxy/haproxy.cfg

# Reload if successful
systemctl reload haproxy

# Or graceful reload
haproxy -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid -sf $(cat /var/run/haproxy.pid)

Check SSL configuration

Verify SSL settings:

# Test TLS connection
openssl s_client -connect example.com:443 -servername example.com

# Test HTTP/2
curl -I --http2 https://example.com

# Test specific TLS version
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3

SSL Labs test

Comprehensive SSL analysis:

# Visit: https://www.ssllabs.com/ssltest/
# Target: A+ rating with modern configuration

See Also

Important Notes

Certificate Format:

HAProxy requires certificate, private key, and chain in a single PEM file. Order: cert → key → intermediates.

SNI Support:

HAProxy uses SNI to select certificates automatically when loading from directory. Ensure clients support SNI.

Graceful Reload:

Use `systemctl reload haproxy` for zero-downtime certificate updates. HAProxy transfers active connections to new process.

OCSP Stapling:

Requires HAProxy 2.2+ for automatic updates. Earlier versions need manual OCSP response management.

HTTP/2:

Requires HAProxy 1.8+ and OpenSSL 1.0.2+. Use `alpn h2,http/1.1` on bind line.

Client Certificates:

For mTLS, use verify required, verify optional, or verify optional_no_ca depending on use case.

Certificate Permissions:

PEM files should be readable only by haproxy user (chmod 600, chown haproxy).

Performance:

HAProxy is extremely efficient at SSL termination. Can handle thousands of concurrent SSL connections on modest hardware.

Documentation:

Official HAProxy docs: haproxy.org/documentation.html

SSL Configuration: cbonte.github.io/haproxy-dconv/configuration-1.9.html#5.1-ssl