1. SSL termination (offloading) #
HAProxy handles decryption to relieve the web servers.
Configuring the certificate #
This is the most common method. HAProxy holds the SSL certificate and communicates in plain HTTP with the backends on a secure private network.
frontend https-in
bind *:443 ssl crt /etc/ssl/certs/mon-site.pem
default_backend app_serversNote: the .pem file must contain the certificate and the private key.
2. SSL passthrough #
Forwarding the encrypted stream without inspection.
Using TCP mode #
If you don't want HAProxy to decrypt traffic (for complete confidentiality), use TCP mode (layer 4).
frontend stream-in
mode tcp
bind *:443
default_backend secure_web3. Forcing HTTPS #
Automatically redirecting users to the secure port.
HTTP to HTTPS redirection #
A simple rule in the HTTP frontend is enough.
frontend http-in
bind *:80
http-request redirect scheme https unless { ssl_fc }Conclusion #
A central point for security.
Performance and security #
By centralizing SSL, you simplify certificate management (e.g. Let's Encrypt) and optimize the performance of your application servers.
Hands-on: certificates and full configuration #
Generate a certificate, configure and test.
Preparing the certificate #
HAProxy expects a single PEM file containing the certificate chain followed by the private key.
# Certificat auto-signé pour tester
openssl req -x509 -newkey rsa:4096 -nodes -days 365 -keyout app.key -out app.crt -subj "/CN=app.exemple.fr"
cat app.crt app.key > /etc/haproxy/certs/app.pem && chmod 600 /etc/haproxy/certs/app.pem
# Vérifier le contenu et la date d'expiration
openssl x509 -in /etc/haproxy/certs/app.pem -noout -subject -enddateHTTPS frontend and redirection #
global
ssl-default-bind-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
frontend https_in
bind *:80
bind *:443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 # un répertoire = plusieurs certificats (SNI)
http-request redirect scheme https unless { ssl_fc }
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
http-request set-header X-Forwarded-Proto https if { ssl_fc }
default_backend app
backend app
balance roundrobin
server web1 10.0.0.11:8080 check
server web2 10.0.0.12:8080 checkTesting and troubleshooting #
haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy
curl -vkI https://app.exemple.fr 2>&1 | grep -E "SSL|TLS|HTTP/"
openssl s_client -connect app.exemple.fr:443 -servername app.exemple.fr </dev/null 2>/dev/null | openssl x509 -noout -dates -issuer
nmap --script ssl-enum-ciphers -p 443 app.exemple.fr # lister les suites acceptées
echo "show ssl cert" | sudo socat stdio /var/run/haproxy.sock # certificats chargés (Runtime API)Good practice: Automate renewal with ACME (see the ACME article) and reload HAProxy without interruption withsystemctl reload haproxyor the Runtime commandset ssl cert.
SSL passthrough #
Leaving encryption to the backends.
Configuration #
With SSL passthrough, HAProxy does not decrypt: it works in TCP mode and routes based on the name requested in the TLS message (SNI). The backends keep their certificates; HAProxy sees neither URLs nor headers.
frontend tls_in
bind :443
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
use_backend app_tls if { req.ssl_sni -i app.example.com }
default_backend web_tls
backend app_tls
mode tcp
server app1 10.0.0.11:443 check| Mode | HAProxy sees HTTP | Certificate on HAProxy | Use |
|---|---|---|---|
| Termination | Yes | Yes | Routing by URL, headers, WAF |
| Re-encryption | Yes | Yes (and on the backends) | End-to-end encryption requirement |
| Passthrough | No | No | Certificates managed by the application, end-to-end mTLS |
