maxuru ~ % cat haproxy/tls-configuration
Configure TLS on HAProxy
/etc/haproxy/haproxy.cfgglobal ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets 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
frontend web bind :443 ssl crt /etc/haproxy/certs/site.pem alpn h2,http/1.1The ssl-default-* settings apply to every bind line that does not override
them, which is what makes this one change rather than one per listener.
Set a floor, don’t stack exclusions
Section titled “Set a floor, don’t stack exclusions”Older configs disable protocol versions one at a time:
# Fragile — each new obsolete version needs another linessl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11That works but describes the past. ssl-min-ver states the floor directly:
ssl-default-bind-options ssl-min-ver TLSv1.2Anything below is refused, including versions nobody has thought about yet. The two forms can be combined, and when they disagree the result is confusing — prefer the floor alone.
TLS 1.2 is the right floor for a general-purpose site in 2026. TLS 1.0 and 1.1 are deprecated by RFC 8996 and rejected by current browsers. A 1.3-only floor is defensible for internal services and will cut off older API clients.
Two cipher settings, and one of them does not do what its name suggests
Section titled “Two cipher settings, and one of them does not do what its name suggests”This trips people up, and it is the same trap as
nginx’s ssl_ciphers:
ssl-default-bind-cipherscontrols TLS 1.2 and below only.ssl-default-bind-ciphersuitescontrols TLS 1.3 (OpenSSL 1.1.1+).
A config that carefully curates ssl-default-bind-ciphers and stops there has
said nothing about TLS 1.3 — which is most of the traffic. The 1.3 suites are
few and all sound, so leaving ciphersuites at its default is fine; the mistake
is believing the first setting covered it.
Both lists come from the Mozilla SSL Configuration Generator, which is worth generating fresh rather than copying from a blog post — cipher recommendations move.
Delete the stapling lines
Section titled “Delete the stapling lines”If your certificates come from Let’s Encrypt, remove these:
# No longer does anything with Let's Encrypt certificates# ssl-default-bind-options ...# (OCSP stapling configuration)Let’s Encrypt shut down its OCSP responder on 2025-08-06 and stopped including OCSP URLs in issued certificates, so there is nothing to fetch and nothing to staple. Revocation is handled through CRL-based mechanisms in the browser instead. The reasoning is the same across every server on this site — see the nginx page, which covers what replaced it.
If your CA still runs a responder, stapling still works and is still worth having.
Certificate file layout
Section titled “Certificate file layout”HAProxy wants the certificate, its chain and the private key concatenated into one PEM file, in that order — which differs from nginx and Apache and is a common first-time stumble:
cat fullchain.pem privkey.pem > /etc/haproxy/certs/site.pemsudo chmod 400 /etc/haproxy/certs/site.pemsudo chown root:root /etc/haproxy/certs/site.pemPointing crt at a directory loads every .pem inside it and selects by SNI,
which is the usual arrangement for more than one site.
Related
Section titled “Related”- nginx OCSP stapling is mostly obsolete — the full explanation of what changed.
- Run HAProxy as a non-root user — why the key stays root-owned.