How to Configure HAProxy to Serve Multiple SSL Certificates for Different Domains


4 views

When implementing HAProxy as a reverse proxy for multiple HTTPS domains, a common challenge arises when trying to serve different SSL certificates for different domain names. The default behavior of HAProxy (prior to version 1.5) was to use the first certificate specified in the configuration for all SSL connections, regardless of the requested domain.

Modern versions of HAProxy (1.5+) support Server Name Indication (SNI), which allows the proxy to select the appropriate certificate based on the client's requested hostname. Here are the key approaches:

Method 1: SNI with Multiple crt Parameters

frontend https-in
    bind :443 ssl crt /etc/ssl/private/example.com.pem crt /etc/ssl/private/api.example.com.pem
    use_backend www_servers if { ssl_fc_sni www.example.com }
    use_backend api_servers if { ssl_fc_sni api.example.com }

Method 2: Using Combined PEM Files

frontend https-in
    bind :443 ssl crt /etc/ssl/private/combined/
    use_backend www_servers if { ssl_fc_sni www.example.com }
    use_backend api_servers if { ssl_fc_sni api.example.com }

Here's a complete working configuration that handles multiple SSL certificates correctly:

global
    log /dev/log local0
    log /dev/log local1 notice
    maxconn 4096
    tune.ssl.default-dh-param 2048
    ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384

defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5000
    timeout client 50000
    timeout server 50000

frontend http-in
    bind *:80
    redirect scheme https code 301 if !{ ssl_fc }

frontend https-in
    bind *:443 ssl crt /etc/ssl/private/www.example.com.pem crt /etc/ssl/private/api.example.com.pem
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    
    acl host_www hdr(host) -i www.example.com
    acl host_api hdr(host) -i api.example.com
    
    use_backend www_servers if host_www
    use_backend api_servers if host_api

backend www_servers
    balance roundrobin
    server web1 10.0.0.1:80 check
    server web2 10.0.0.2:80 check

backend api_servers
    server api1 10.0.1.1:8080 check
    server api2 10.0.1.2:8080 check

Each certificate file must contain:

-----BEGIN RSA PRIVATE KEY-----
[private key content]
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
[primary certificate content]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[intermediate CA certificate]
-----END CERTIFICATE-----

To verify that the correct certificates are being served:

openssl s_client -connect www.example.com:443 -servername www.example.com | openssl x509 -noout -subject
openssl s_client -connect api.example.com:443 -servername api.example.com | openssl x509 -noout -subject

When using multiple certificates:

  • Keep your certificate chain optimized (include only necessary intermediates)
  • Use elliptic curve certificates for better performance
  • Consider OCSP stapling to reduce handshake time

If certificates aren't being served correctly:

  1. Verify HAProxy version supports SNI (1.5+)
  2. Check certificate file permissions (haproxy user needs read access)
  3. Confirm the certificate files contain both private key and full chain
  4. Test with OpenSSL commands before troubleshooting browser behavior

When handling multiple domains with distinct SSL certificates in HAProxy, the key is proper Server Name Indication (SNI) implementation. Your current configuration applies the first certificate to all requests because HAProxy needs explicit domain-to-certificate mapping.

For HAProxy 1.5+ with OpenSSL 1.0.2+, use this optimized configuration:

frontend https-in
    bind :443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
    http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;"
    
    # Certificate per domain
    use_backend www_backend if { ssl_fc_sni www.example.com }
    use_backend api_backend if { ssl_fc_sni api.example.com }

Organize certificates properly for HAProxy's automatic loading:

/etc/haproxy/certs/
├── www.example.com.pem
├── api.example.com.pem
└── dhparams.pem

Each .pem file should contain (in order):

  1. Private key
  2. Certificate
  3. Intermediate certificates

Here's a production-ready configuration:

global
    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

frontend https_frontend
    bind :443 ssl crt /etc/haproxy/certs/ strict-sni
    
    # ACLs for SNI matching
    acl is_www hdr(host) -i www.example.com
    acl is_api hdr(host) -i api.example.com
    
    # Backend selection
    use_backend www_servers if is_www
    use_backend api_servers if is_api

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

backend www_servers
    server server1 10.0.1.10:443 check ssl verify none

backend api_servers
    server server1 10.0.2.10:443 check ssl verify none

Use these OpenSSL commands to verify proper certificate serving:

# Test www domain
openssl s_client -connect your.haproxy:443 -servername www.example.com | openssl x509 -noout -subject

# Test api domain
openssl s_client -connect your.haproxy:443 -servername api.example.com | openssl x509 -noout -subject

When dealing with multiple certificates:

  • Enable SSL session caching (ssl-default-server-cache)
  • Consider OCSP stapling with ssl ocsp-response
  • Enable SSL compression only if absolutely necessary