Last updated: 2026-06-25
HAProxy TLS/SSL Configuration Guide
This guide provides recommended TLS/SSL settings for HAProxy, a widely used high-performance load balancer and reverse proxy. HAProxy handles TLS termination at the frontend, offloading encryption from backend servers.
Prerequisites
- HAProxy 2.8 or later (3.x is the current LTS line)
- OpenSSL 1.1.1 or later
- A valid SSL/TLS certificate (PEM format with certificate and key combined, or separate files)
HAProxy expects the certificate and private key in a single PEM file by default:
cat fullchain.pem privkey.pem > /etc/haproxy/certs/example.com.pem
chmod 600 /etc/haproxy/certs/example.com.pem
Global TLS Defaults
Set secure TLS defaults in the global section of your HAProxy configuration (/etc/haproxy/haproxy.cfg). These apply to all frontends unless overridden.
Protocol Versions
Set the minimum TLS version to 1.2 and disable session tickets:
global
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets
- ssl-default-bind-options - Applies to incoming client connections (frontends).
- ssl-default-server-options - Applies to outgoing connections to backend servers.
Cipher Suites
Configure strong cipher suites for TLS 1.2 and TLS 1.3:
global
ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
ssl-default-bind-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
ssl-default-server-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
ssl-default-server-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
- ssl-default-bind-ciphers - Sets TLS 1.2 cipher suites for client-facing connections.
- ssl-default-bind-ciphersuites - Sets TLS 1.3 cipher suites for client-facing connections.
Frontend Configuration
Configure your HTTPS frontend with the certificate and ALPN protocol negotiation:
frontend https
bind :::443 v4v6 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
mode http
# HSTS header
http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
default_backend servers
The alpn h2,http/1.1 parameter enables HTTP/2 and HTTP/1.1 negotiation via ALPN.
Using a Certificate Directory
If you serve multiple domains, point to a directory containing all certificate PEM files:
frontend https
bind :::443 v4v6 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
HAProxy will automatically match certificates to hostnames via SNI.
HTTPS Redirect
Redirect all HTTP traffic to HTTPS:
frontend http
bind :::80 v4v6
mode http
http-request redirect scheme https code 301
Backend TLS (Re-encryption)
If your backends also require TLS (end-to-end encryption), configure the backend with TLS verification:
backend servers
mode http
server web1 10.0.0.1:443 ssl verify required ca-file /etc/haproxy/ca.pem check
server web2 10.0.0.2:443 ssl verify required ca-file /etc/haproxy/ca.pem check
Use verify required with a ca-file to validate backend server certificates. Use verify none only if backends are on a fully trusted network.
Mutual TLS (mTLS)
Standard TLS authenticates only the server: the client verifies the server's certificate, but the server does not verify the client. Mutual TLS adds client authentication, requiring the connecting client to also present a certificate. This is an optional hardening step, not required for standard load balancing. It is most useful for internal APIs and services where all connecting clients are under your control.
To require client certificates on the frontend, add ca-file and verify required to the bind line:
frontend https
bind :::443 v4v6 ssl crt /etc/haproxy/certs/example.com.pem \
ca-file /etc/haproxy/ca.crt \
verify required \
alpn h2,http/1.1
default_backend servers
- ca-file - CA certificate used to verify client certificates.
- verify required - Reject connections that do not present a valid client certificate.
- verify optional - Allow connections without a client certificate; use ACLs to inspect the result.
Use ACLs to enforce or route based on client certificate properties:
acl client_cert_valid ssl_c_used
http-request deny unless client_cert_valid
Forward the client certificate's DN to backend applications as a header:
http-request set-header X-SSL-Client-DN %[ssl_c_s_dn]
http-request set-header X-SSL-Client-CN %[ssl_c_s_dn(cn)]
See RFC 8446 ยง4.3.2 for the TLS Certificate Request specification, and Wikipedia: Mutual authentication for a general overview.
OCSP Stapling
HAProxy supports OCSP stapling. Generate an OCSP response and reference it on the bind line:
frontend https
bind :::443 v4v6 ssl crt /etc/haproxy/certs/example.com.pem crt-list /etc/haproxy/crt-list.cfg alpn h2,http/1.1
In the certificate list file (crt-list.cfg):
/etc/haproxy/certs/example.com.pem [ocsp-update on] example.com
Note: ocsp-update on requires HAProxy 2.8+. On older versions, use openssl ocsp to fetch responses manually and load them via the HAProxy runtime API.
Complete Configuration Example
global
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
ssl-default-bind-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
ssl-default-server-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
frontend http
bind :::80 v4v6
http-request redirect scheme https code 301
frontend https
bind :::443 v4v6 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
default_backend servers
backend servers
server web1 10.0.0.1:8080 check
server web2 10.0.0.2:8080 check
Security Notes
The cipher suite and protocol configuration in this guide addresses the following known TLS vulnerabilities:
- POODLE (CVE-2014-3566, 2014): SSL 3.0 is disabled. TLS_FALLBACK_SCSV was added in OpenSSL 1.0.1j / 1.0.2 (October 2014); SSL 3.0 disabled by default in OpenSSL 1.1.0 (August 2016).
- BEAST (CVE-2011-3389, 2011): Mitigated by recommending TLS 1.2 as the minimum; AEAD-only ciphers eliminate the CBC padding oracle.
- CRIME (CVE-2012-4929, 2012): TLS compression is off by default in OpenSSL 1.1.0+; do not enable it.
- Lucky13 (2013): AEAD-only cipher list eliminates CBC padding timing side-channels entirely.
- FREAK (CVE-2015-0204, 2015): EXPORT-grade ciphers are excluded from the cipher string. Removed from OpenSSL 1.1.0 (August 2016).
- LOGJAM (CVE-2015-4000, 2015): Short-key DHE is excluded; only ECDHE key exchange is recommended.
- Sweet32 (CVE-2016-2183, 2016): 3DES is excluded from the cipher string.
- ROBOT (2017): Static RSA key exchange is excluded; only ECDHE is recommended.
- Downgrade attacks: TLS_FALLBACK_SCSV prevents protocol version rollback.
- Renegotiation injection (CVE-2009-3555, 2009): Secure renegotiation is enforced by default in OpenSSL 0.9.8m+; TLS 1.3 removes renegotiation entirely.
The following are not addressable through TLS configuration alone:
- Heartbleed (CVE-2014-0160, 2014): A memory disclosure bug in OpenSSL 1.0.1 through 1.0.1f. Fixed in OpenSSL 1.0.1g (April 7, 2014). Addressed by patching OpenSSL, not by TLS configuration.
- BREACH (CVE-2013-3587, 2013): Exploits HTTP-level response compression (gzip/deflate on responses). Mitigated at the application layer by disabling HTTP compression or using BREACH countermeasures; TLS configuration cannot prevent it.
- DROWN (CVE-2016-0800, 2016): Requires SSLv2 to be enabled on any server sharing the same private key. Ensure SSLv2 is disabled on all services that use the same certificate and key pair.
Verification
Test your configuration before reloading:
haproxy -c -f /etc/haproxy/haproxy.cfg
systemctl reload haproxy
Check the active TLS settings:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject
Test your configuration externally with the Mr.DNS SSL/TLS Certificate Check.