HAProxy IIS Certificate Management

CA Bundles in HAProxy and IIS: What They Are and How to Stop Managing Them Manually

May 5, 2026 · 8 min read

The most common reason a certificate causes errors even after it's been correctly issued is an incomplete chain. HAProxy and IIS handle chain assembly differently — and getting it wrong produces errors that are easy to misdiagnose.

You've obtained a TLS certificate. It's valid, issued by a trusted CA, not expired. You deploy it to HAProxy or IIS. Clients still get errors.

The most common cause: the certificate chain is incomplete. The server is presenting only the end-entity certificate, without the intermediate certificates that connect it to a root CA that clients trust. Browsers and TLS clients require the full chain. When the chain is incomplete, they can't verify trust — so they reject the connection.

Understanding CA bundles is understanding certificate chains. And certificate chains behave differently on HAProxy and IIS, which is why the same mistake produces different errors on each platform.

What a certificate chain is and why it matters

When a Certificate Authority (CA) like Let's Encrypt or DigiCert issues your TLS certificate, it doesn't sign it with a root certificate directly. Root certificates are offline, highly protected, and used sparingly. Instead, the CA uses an intermediate certificate — itself signed by the root — to sign your certificate.

The result is a chain of three certificates:

  1. Your end-entity certificate — the cert for your domain, signed by the intermediate
  2. The intermediate certificate — issued by the CA, signed by the root, trusted by the root
  3. The root certificate — pre-installed in browsers, operating systems, and trust stores worldwide

When a client connects to your server, it receives your end-entity cert. It then needs to build a path from that cert to a trusted root. If you send only the end-entity cert, the client has to find the intermediate certificate on its own — by fetching it from the URL specified in the cert's AIA (Authority Information Access) field, if that field exists. Some clients do this; many don't, especially in high-latency or air-gapped environments.

The reliable solution is to include the intermediate certificates in what you serve. The collection of intermediate (and sometimes root) certificates served alongside your end-entity cert is the CA bundle, also called the certificate chain.

What HAProxy needs: the combined PEM file

HAProxy handles TLS certificates differently from most web servers. It expects a single PEM file that contains, in order:

  1. The private key
  2. The end-entity certificate
  3. Any intermediate certificates (from the issuing intermediate up toward the root)

This combined file is what HAProxy refers to when you specify a certificate in its config:

# haproxy.cfg

frontend https-in

bind *:443 ssl crt /usr/local/etc/haproxy/certs/api.pem

The api.pem file at that path must include all three components — key, cert, and chain — concatenated in the right order. A file that contains only the end-entity cert without the chain will cause HAProxy to serve an incomplete chain to clients.

Assembling this file from what a CA delivers is straightforward but manual:

# Assemble HAProxy PEM from Let's Encrypt components

cat privkey.pem fullchain.pem > /usr/local/etc/haproxy/certs/api.pem

# Let's Encrypt's fullchain.pem already combines cert + chain,

# so this is the full combined file HAProxy expects.

The key detail: fullchain.pem (what Let's Encrypt produces) already includes both the end-entity cert and the intermediate chain concatenated. cert.pem alone does not — it's only the end-entity cert. Using cert.pem instead of fullchain.pem in your HAProxy PEM file is the most common source of incomplete chain errors on HAProxy.

What IIS needs: the certificate store

IIS handles TLS certificates through the Windows Certificate Manager, not through PEM files. The installation process and chain assembly work differently.

When you import a certificate into the Windows certificate store, Windows can often build the chain automatically by following AIA extension URLs in the certificate to download intermediate certificates. This usually works — but it depends on the server having internet access to fetch the intermediates, and it can fail in air-gapped or restricted-network environments.

For reliable chain installation on IIS, the recommended approach is to import the certificate as a PKCS#12 (.pfx) file that bundles the private key, end-entity certificate, and intermediate certificates together. The certificate authority or ACME client typically provides the means to export in this format.

WIN-ACME — the standard ACME client for IIS — handles this correctly by default. When it installs a certificate, it imports the full chain into the Windows certificate store and binds it to the appropriate IIS site. The chain assembly is handled by WIN-ACME, not manually by the operator. This is one of the strongest arguments for using WIN-ACME on IIS rather than downloading and importing certificates by hand: the chain is never incomplete because WIN-ACME doesn't give you the option to get it wrong.

How to diagnose an incomplete chain

If clients are getting TLS errors and you've confirmed the certificate itself is valid and not expired, an incomplete chain is the most likely culprit. Two diagnostic approaches:

Using OpenSSL:

# Check what chain the server is actually serving

openssl s_client -connect your-domain.com:443 -showcerts

# Look for multiple "CERTIFICATE" blocks in the output.

# One block = only end-entity cert (incomplete chain).

# Two or more = end-entity + intermediate(s) (correct).

Using SSL Labs: Run a scan at ssllabs.com/ssltest. The result shows the chain the server presents and explicitly flags incomplete chains as an error. The "Chain Issues" field will show "Incomplete" if intermediates are missing.

A correctly configured HAProxy or IIS server will show at least two certificates in the s_client output: the end-entity cert followed by one or more intermediate certs. If you see only one, the chain is missing.

When intermediate CAs rotate

Certificate chains aren't static. CAs periodically rotate their intermediate certificates — issuing new intermediates from the same root or from different roots, usually to manage key lifetimes or respond to security requirements.

When an intermediate rotates, your end-entity certificate is re-issued by the new intermediate. Your CA bundle needs to contain the new intermediate, not the old one. This is a subtle renewal-time failure mode: the cert renews successfully, but the chain file is assembled with a cached copy of the old intermediate. Clients that have already fetched and cached the new intermediate (or that do AIA chasing) may be fine; clients that rely on your server to present the complete chain will see a trust error.

The reliable fix is to always assemble the chain from the files your ACME client or CA delivers at renewal time, not from a cached or pre-existing chain file. Let's Encrypt's fullchain.pem always reflects the current intermediate. WIN-ACME always fetches the current chain from the CA. Don't shortcut this by reusing a chain file across renewal cycles.

Why manual chain assembly is a reliability problem

The steps for assembling an HAProxy PEM file are simple enough to do once. The problem is doing them correctly every single time, on every renewal, across every cert in your inventory.

At 47-day maximum certificate lifetimes — the direction the CA/Browser Forum is heading — each cert in a modestly sized infrastructure requires roughly 8 renewals per year. Each renewal needs the chain assembled correctly. Manual assembly at this frequency, across multiple certs and potentially multiple team members, produces errors.

ACME-native tools eliminate this. HAProxy's native ACME client fetches the cert and chain directly from the ACME endpoint and assembles the combined PEM file itself. WIN-ACME on IIS imports the full chain from the CA without operator involvement. When certificate delivery goes through an ACME endpoint — such as the CertLocker Gateway — the client handles chain assembly as part of the standard ACME certificate fetch. The operator never touches the raw PEM files.

Summary

CA bundles and certificate chains are the plumbing that makes TLS trust work. The key points for HAProxy and IIS operators:

  • HAProxy requires a single combined PEM file: private key + end-entity cert + intermediate chain, in that order
  • Use fullchain.pem (not cert.pem) when assembling HAProxy PEM files from Let's Encrypt output
  • IIS handles chain installation through the Windows certificate store; import as PKCS#12 for reliable chain inclusion
  • WIN-ACME handles IIS chain assembly automatically — prefer it over manual import
  • If clients are getting trust errors on a valid, unexpired cert, run openssl s_client -showcerts to check whether the chain is being served
  • When intermediates rotate at renewal time, always use the fresh chain from the CA, not a cached one

At 47-day certificate lifetimes, the operational cost of manual chain assembly compounds quickly. ACME-native tooling — HAProxy's native ACME client, WIN-ACME on IIS, any standards-compliant ACME client — handles chain assembly as part of issuance. That's the direction worth moving toward.

ACME delivery that handles chain assembly for you

CertLocker's Gateway delivers certificates via ACME. HAProxy native ACME and WIN-ACME on IIS handle chain assembly automatically — no manual PEM concatenation required.