Skip to main content

    How to Install an SSL Certificate on HAProxy (SSL Termination Guide)

    Install an SSL certificate on HAProxy: build the combined PEM, configure the HTTPS bind line, force HTTPS, harden TLS, and re-encrypt to your backends.

    MS
    My-SSL Security Team
    ·
    15 min read
    ·
    Published July 1, 2026

    HAProxy sits in front of your servers as a load balancer and reverse proxy, which makes it the natural place to handle HTTPS for everything behind it. Instead of installing a certificate on every backend, you install it once on HAProxy and let it terminate TLS at the edge. The catch is that HAProxy configures certificates differently from a normal web server: there's no "certificate file" and "key file" pair — it wants a single combined PEM referenced on a bind line. This guide walks through the whole process on HAProxy 3.x: building that PEM, wiring up the HTTPS frontend, forcing HTTPS, hardening the TLS settings, optionally re-encrypting to your backends, and automating renewal.

    The short version

    Concatenate your certificate, intermediate chain, and private key into one .pem, reference it with bind *:443 ssl crt /etc/haproxy/certs/site.pem, add a one-line HTTP→HTTPS redirect, set a modern TLS floor and HSTS, then reload. If the internal hop to your backends isn't fully trusted, add ssl verify required to the server lines to encrypt that leg too.

    What HAProxy does with your certificate

    HAProxy runs your public HTTPS endpoint in a frontend and forwards requests to a pool of servers defined in a backend. When you attach a certificate to the frontend, HAProxy performs the full TLS handshake with the visitor's browser, decrypts the request, and then hands it to a backend server. That's SSL termination: the encrypted tunnel ends at HAProxy, and the expensive cryptographic work happens in one place instead of on every server behind it.

    Because HAProxy can read the decrypted request, it can route by URL or hostname, add headers, and balance load intelligently — the same reason most large sites terminate at the edge. What you decide separately is whether the hop from HAProxy to your backends stays plaintext (plain offloading) or gets re-encrypted (SSL bridging). We cover both below; if you want the conceptual background first, the TLS termination explainer lays out the three models and the plaintext-backend risk in detail.

    Before you start

    This guide assumes HAProxy 3.x (the current LTS line is 3.4, released June 2026, though the steps here work on 2.x too). You'll need:

    • Root or sudo access to the machine running HAProxy, and the ability to reload the service.
    • Your issued certificate and its private key. After you complete CSR generation and validation, your CA sends back the leaf certificate plus one or more intermediate certificates. You keep the private key you generated with the CSR.
    • OpenSSL available on the box, for combining and inspecting files. Our OpenSSL cheat sheet has the conversion commands if your certificate arrived as a .pfx or in another format.
    • Backend servers already defined in your HAProxy config (or ready to add). We'll add TLS to the frontend and, optionally, to the backend.

    Step 1 — Build the combined PEM file

    This is the step that trips people up coming from Nginx or Apache. HAProxy wants one file containing your leaf certificate, the intermediate chain, and the private key, all concatenated. Assuming your CA gave you example.com.crt (leaf), chain.crt (intermediates), and you already hold example.com.key (private key):

    # Create a certs directory HAProxy can read
    sudo mkdir -p /etc/haproxy/certs
    
    # Concatenate leaf + intermediate chain + private key into one PEM
    cat example.com.crt chain.crt example.com.key \
      | sudo tee /etc/haproxy/certs/example.com.pem > /dev/null
    
    # Lock it down — it contains your private key
    sudo chmod 600 /etc/haproxy/certs/example.com.pem
    sudo chown root:root /etc/haproxy/certs/example.com.pem

    HAProxy parses every PEM block in the file, so the exact ordering is forgiving, but certificate → intermediate chain → key is the conventional and most portable order. The one thing you cannot skip is the intermediate chain: leave it out and HAProxy will still start, desktop Chrome may still work (it often caches intermediates), but mobile and API clients will reject the certificate with a chain error. If you're unsure the chain is right, verify it after configuring with the OpenSSL command in the last section.

    Got a .pfx / PKCS#12 instead? Convert it first: openssl pkcs12 -in cert.pfx -out example.com.pem -nodes extracts the certificate, chain, and key into a single PEM in one shot — then move it into your certs directory and lock down the permissions as above.

    Step 2 — Configure the HTTPS frontend

    Open /etc/haproxy/haproxy.cfg and point a frontend's bind line at the PEM you just built. The ssl keyword enables TLS termination and crt names the certificate:

    frontend https_in
        bind *:443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
        mode http
        default_backend web_servers
    
    backend web_servers
        mode http
        balance roundrobin
        server web1 10.0.0.11:80 check
        server web2 10.0.0.12:80 check

    The alpn h2,http/1.1 option advertises HTTP/2 (with a graceful fallback to HTTP/1.1) during the TLS handshake, so modern browsers get the faster protocol. At this point HAProxy terminates HTTPS on port 443 and forwards plain HTTP to your two backend servers on port 80.

    Serving several sites? Point crt at a directory instead of a single file — bind *:443 ssl crt /etc/haproxy/certs/ — and HAProxy loads every PEM in it, then selects the correct one per request using SNI. A single wildcard or multi-domain (SAN) certificate can also cover many hostnames from one PEM.

    Step 3 — Redirect HTTP to HTTPS

    Terminating HTTPS on 443 doesn't stop visitors reaching you on plain HTTP. Add a frontend on port 80 that sends every request straight to HTTPS with a permanent redirect:

    frontend http_in
        bind *:80
        mode http
        http-request redirect scheme https unless { ssl_fc }

    The { ssl_fc } condition is true when the connection already arrived over TLS, so unless { ssl_fc } means "redirect to HTTPS unless it's already HTTPS" — which is what prevents redirect loops. HAProxy issues this as a 301 (permanent) redirect by default, the correct choice so search engines consolidate ranking on your HTTPS URLs. If you run HAProxy behind Cloudflare or another proxy, the same X-Forwarded-Proto caveat from our HTTP-to-HTTPS redirect guide applies — trust the header from the upstream proxy so you don't double-redirect.

    Step 4 — Harden TLS and set HSTS

    Out of the box HAProxy will negotiate older protocols. Because every public connection terminates here, this one file defines your whole site's TLS posture — so set a modern floor once, in the global section, and it applies to every bind:

    global
        # Minimum TLS 1.2; disable stateful session tickets
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
        # Paste a current cipher list from the Mozilla SSL Config Generator
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...

    Rather than memorize a cipher string that shifts over time, generate a current one for HAProxy from the Mozilla SSL Configuration Generator — pick "Intermediate" for broad compatibility, or "Modern" if you can require TLS 1.3-only clients. TLS 1.3's cipher suites are negotiated separately and are safe defaults, so the cipher list above mainly governs TLS 1.2.

    Then add HSTS to the HTTPS frontend so browsers refuse to talk to you over plain HTTP on future visits, closing the gap of that first unprotected request:

    frontend https_in
        bind *:443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
        mode http
        http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        default_backend web_servers

    Add includeSubDomains and preload deliberately

    The includeSubDomains directive applies HTTPS-only to every subdomain, and preload asks browsers to hardcode your domain as HTTPS-only. Both are hard to undo quickly — only enable them once you're certain every subdomain can serve HTTPS. The HSTS guide covers the preload-list requirements in full.

    Step 5 — Forward the protocol and client IP

    Because HAProxy forwards plain HTTP to the backends, your application sees an insecure request coming from HAProxy's own address — not the visitor's. Two headers fix the two symptoms this causes:

    frontend https_in
        bind *:443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
        mode http
        # Preserve the original client IP for logs and rate limits
        option forwardfor
        # Tell the backend the original request was HTTPS
        http-request set-header X-Forwarded-Proto https
        http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        default_backend web_servers
    • option forwardfor adds an X-Forwarded-For header with the real client IP, so your backend logs, analytics, and rate limits don't collapse every visitor into HAProxy's single address.
    • X-Forwarded-Proto https tells the application the original request was secure. Without it, apps often build redirects and absolute URLs as http://, which is the classic cause of redirect loops behind a terminating proxy. Configure your framework to trust this header.

    Step 6 — Re-encrypt to the backend (optional)

    Everything so far leaves the hop from HAProxy to your backends in clear HTTP. On a single, isolated, well-controlled network segment that can be acceptable. But if the data is sensitive or regulated, or the internal network isn't fully trusted, re-encrypt that leg — SSL bridging — by adding TLS to the backend server lines:

    backend web_servers
        mode http
        balance roundrobin
        # ssl enables TLS to the backend; verify required validates its certificate
        server web1 10.0.0.11:443 ssl verify required ca-file /etc/haproxy/ca-certs.pem check
        server web2 10.0.0.12:443 ssl verify required ca-file /etc/haproxy/ca-certs.pem check

    The ssl keyword opens a fresh TLS connection to each backend, and — this part matters — verify required with a ca-file makes HAProxy actually validate the backend's certificate against a trusted CA rather than accepting any certificate blindly. Skipping verification gives you encryption without authentication, which defeats much of the point. The backends need their own certificates on that internal hop, but since only HAProxy ever sees them, those can be privately-issued or self-signed certificates from an internal CA — the public only ever sees the certificate on HAProxy's frontend.

    Which model do you need? Handling payment or health data usually means re-encrypt, because strong cryptography is required across all networks the data crosses, not just the public internet. For internal-only services on a demonstrably private segment, plain termination is a common and reasonable choice. Decide it on purpose rather than accepting the default — the termination vs bridging comparison walks through the trade-off.

    Automating renewal with ACME

    Certificate lifetimes are short and getting shorter, so renewal has to be automatic. With HAProxy you have two routes:

    • An external ACME client. The established approach is a tool like acme.sh or Certbot that obtains and renews the certificate, rebuilds the combined PEM in your certs directory, and reloads HAProxy. Our Certbot & ACME automation guide covers the deploy-hook pattern that ties issuance to a reload.
    • HAProxy's native ACME client. Recent HAProxy versions ship built-in ACME support — experimental HTTP-01 challenge handling landed in HAProxy 3.2 and was refined in later releases so HAProxy can request and renew certificates itself, without an external client. On a fresh install it needs a placeholder certificate to start before the first real one is issued.

    Either way, free automated certificates and paid certificates encrypt traffic identically — the differences are validation level, lifetime, and support, which our Let's Encrypt vs paid SSL comparison breaks down if you're choosing between them for a HAProxy deployment.

    Reload, verify, and troubleshoot

    Always validate the configuration before reloading — HAProxy will tell you the exact line it's unhappy about instead of failing to restart:

    # Validate the config file
    sudo haproxy -c -f /etc/haproxy/haproxy.cfg
    
    # Apply it with a graceful reload (keeps existing connections)
    sudo systemctl reload haproxy
    
    # Confirm the served chain from another machine
    openssl s_client -connect example.com:443 -servername example.com

    In the openssl s_client output, check that the certificate chain lists your leaf certificate and its intermediate, and that the final line reads Verify return code: 0 (ok). The most common problems and their fixes:

    • "Cannot find the certificate" / won't start: check the crt path and that the HAProxy process can read the file (root-owned, 600). Run the -c check above to see the precise error.
    • Mobile or API clients reject the certificate but Chrome accepts it: the PEM is missing its intermediate. Re-run Step 1 with the CA's chain file included, then reload. This is the single most frequent HAProxy SSL certificate error.
    • Redirect loop: your app is redirecting to HTTP because it doesn't know the request was secure. Confirm X-Forwarded-Proto https is set and that your framework trusts it.

    That's a complete, hardened HAProxy TLS setup: one PEM, a terminating frontend, forced HTTPS, a modern TLS floor with HSTS, optional end-to-end re-encryption, and automated renewal. If you also run individual web servers behind HAProxy and want them to terminate TLS themselves, the same certificate is put to work in our Nginx and Apache installation guides. Not sure which certificate to buy for the job? The step-by-step chooser walks through single-domain, wildcard, and multi-domain options.

    Frequently Asked Questions

    Get instant answers to common questions about SSL certificates and our services.

    Still Have Questions?

    Our SSL experts are available 24/7 to help with any questions about certificates, installation, or technical issues.