CertTools Logo
CertTools
Monitoring
References

CertTools

HAProxy SSL/TLS Configuration

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

← Back to All References

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

Backend SSL/TLS Connections

SSL to backend servers

# Terminate SSL at HAProxy, re-encrypt to backend
backend secure_backend
    balance roundrobin

    # Connect to backend via SSL
    server web1 192.168.1.10:443 ssl verify required ca-file /etc/haproxy/ca/backend-ca.pem check
    server web2 192.168.1.11:443 ssl verify required ca-file /etc/haproxy/ca/backend-ca.pem check

Use when backend servers require encrypted connections and certificate validation.

SSL without verification

backend backend_no_verify
    balance roundrobin

    # SSL to backend without certificate verification (not recommended for production)
    server web1 192.168.1.10:443 ssl verify none check
    server web2 192.168.1.11:443 ssl verify none check

Useful for testing or trusted internal networks, but verify required is preferred.

Backend client certificate authentication

backend mtls_backend
    balance roundrobin

    # Present client certificate to backend
    server web1 192.168.1.10:443 ssl verify required ca-file /etc/haproxy/ca/backend-ca.pem crt /etc/haproxy/client-certs/haproxy-client.pem check

HAProxy presents a client certificate when connecting to backend servers requiring mTLS.

Health Checks with TLS

HTTP health checks over SSL

backend web_backend
    balance roundrobin

    # Health check configuration
    option httpchk GET /health
    http-check expect status 200

    # Servers with SSL health checks
    server web1 192.168.1.10:443 ssl verify none check check-ssl
    server web2 192.168.1.11:443 ssl verify none check check-ssl

Use check-ssl to perform health checks over SSL/TLS connections.

SSL health check with SNI

backend web_backend
    balance roundrobin
    option httpchk GET /health HTTP/1.1\r\nHost:\ example.com

    # Include SNI in health checks
    server web1 192.168.1.10:443 ssl verify none check check-ssl sni str(example.com)
    server web2 192.168.1.11:443 ssl verify none check check-ssl sni str(example.com)

Send SNI hostname during health checks for virtual host environments.

TCP health checks for SSL

backend web_backend
    mode tcp
    balance roundrobin

    # Simple TCP connection check
    option tcp-check

    server web1 192.168.1.10:443 check
    server web2 192.168.1.11:443 check

Basic TCP connectivity check without SSL handshake inspection.

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

Let's Encrypt Certificate Management

Obtain certificates with certbot

# Install certbot
apt-get install certbot

# Obtain certificate using webroot
certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com

# Or standalone mode (stops HAProxy temporarily)
systemctl stop haproxy
certbot certonly --standalone -d example.com -d www.example.com
systemctl start haproxy

Webroot method allows certificate issuance without stopping HAProxy.

Convert to HAProxy format

# Combine Let's Encrypt files into HAProxy PEM format
cat /etc/letsencrypt/live/example.com/fullchain.pem \
    /etc/letsencrypt/live/example.com/privkey.pem \
    > /etc/haproxy/certs/example.com.pem

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

# Reload HAProxy
systemctl reload haproxy

HAProxy requires certificate and private key in single file.

Automatic renewal script

# Create renewal hook script: /etc/letsencrypt/renewal-hooks/deploy/haproxy.sh
#!/bin/bash
DOMAIN="example.com"
cat /etc/letsencrypt/live/${DOMAIN}/fullchain.pem \
    /etc/letsencrypt/live/${DOMAIN}/privkey.pem \
    > /etc/haproxy/certs/${DOMAIN}.pem
chmod 600 /etc/haproxy/certs/${DOMAIN}.pem
systemctl reload haproxy

# Make executable
chmod +x /etc/letsencrypt/renewal-hooks/deploy/haproxy.sh

# Test renewal
certbot renew --dry-run

Deploy hook automatically updates HAProxy certificates after renewal.

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

Nginx SSL Config
Alternative web server SSL setup
Apache SSL Config
Apache web server SSL setup
certbot Commands
Obtain Let's Encrypt certificates
acme.sh Commands
Alternative ACME client

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