You deploy a new certificate, open the site in your browser, and the padlock is right there. Everything looks fine. Then a teammate reports that the API behind the same domain throws a certificate error from their script. A monitoring check fails. A payment webhook from an external service stops arriving. The certificate is valid, it hasn't expired, and yet half your clients can't connect.

Nine times out of ten, the culprit is the certificate chain, specifically a chain your server didn't send in full. Browsers quietly paper over the gap. Almost nothing else does. Understanding why means understanding what a chain actually is, what your server is responsible for sending, and where the responsibility ends.

A public certificate is never trusted on its own. It's trusted because it can be traced back, signature by signature, to a root certificate your device already carries. That trace is the chain, and it has three kinds of link.

The leaf (or end-entity certificate) is the one issued for your domain. It's what your browser is really trying to verify. The root sits at the opposite end: a self-signed certificate belonging to a certificate authority, baked into the trust stores that Apple, Microsoft, Google, and Mozilla ship with every device. Between them sit one or more intermediate certificates. The root signs an intermediate, the intermediate signs your leaf, and each signature is a link a client can verify:

root  →  intermediate  →  leaf (your domain)

The reason for the middle layer is risk management. A root's private key is worth so much that it's kept offline in a vault and used as rarely as possible. The day-to-day work of signing customer certificates is delegated to intermediates. If an intermediate key is ever compromised, it can be revoked and replaced without disturbing the root that billions of devices depend on. This design is the same chain-of-trust idea behind how SSL certificates work generally; here we're zooming in on the part that actually breaks in production.

What your server is supposed to send#

Here's the rule that trips people up. During the TLS handshake, your server has to present not just the leaf, but every intermediate between the leaf and a trusted root. It does not send the root itself.

That asymmetry is deliberate. The client already has the root in its trust store; re-sending it would be wasted bytes, and a client would never trust a root just because a server offered one. So the server's job is to hand over the leaf plus the intermediate(s), in order, with the leaf first. The client then supplies the final link, the root, from its own local store and checks that the signatures connect end to end.

If you've used Let's Encrypt or another ACME issuer, this is exactly why the tooling gives you two files. There's a cert.pem containing only your leaf, and a fullchain.pem containing your leaf followed by the intermediate. Configure your server with the leaf alone and you've built an incomplete chain. Configure it with fullchain.pem and the chain is complete. That one-file difference is behind a large share of real-world TLS failures.

The missing-intermediate trap#

So why does an incomplete chain load fine in Chrome and fail everywhere else? Because clients differ in how hard they'll work to rescue a broken chain.

A certificate can carry a pointer to where its issuer's certificate lives, in an extension called Authority Information Access (AIA). When a browser receives a leaf whose issuing intermediate is missing from the handshake, most browsers will read that pointer, fetch the missing intermediate over HTTP, and complete the chain on their own. Some browsers also keep a preloaded cache of common intermediates and pull from that. Either way, the user sees a working padlock and never learns anything was wrong.

Non-browser clients generally do none of this. Command-line tools like curl and openssl, most programming-language HTTP libraries, and many mobile and server runtimes verify strictly against the certificates actually presented. Hand them an incomplete chain and they stop with a verification error, because from their point of view the leaf leads to an intermediate they were never given and can't get to a trusted root. The site isn't broken for browsers and working for scripts by accident. It's the AIA-fetching difference, quietly deciding who gets rescued.

This is what makes incomplete chains so easy to ship and so painful to debug: the person deploying almost always checks in a browser, sees green, and moves on. The failure surfaces later, in an integration nobody thought to test.

When the chain has more than one path#

Chains would be simple if every leaf had exactly one route to exactly one root. In practice, a single intermediate is often cross-signed, meaning more than one root has vouched for it. That creates alternate paths a client can take to reach a trust anchor, and it exists to solve a genuine problem: a brand-new certificate authority whose own root isn't yet in older devices.

The clearest real example is Let's Encrypt. Its own root, ISRG Root X1, is now trusted almost everywhere, but for years it was too new to be present on older hardware. To bridge the gap, an older, widely-trusted root operated by IdenTrust, DST Root CA X3, cross-signed Let's Encrypt's own root. Devices that didn't yet trust ISRG Root X1 directly could still validate the chain by following it up to the older DST root instead.

That older root eventually reached its own end. DST Root CA X3 expired on September 30, 2021, and the moment it did, clients that still relied on the cross-signed path (most notably Android devices older than version 7.1.1, which had never received ISRG Root X1) began seeing certificate errors across a huge swath of the web, even though the actual site certificates were perfectly valid. Newer devices were unaffected because they could reach the modern root directly. It was a vivid demonstration that a "valid certificate" is only as good as the trust anchor the client can actually reach at the far end of the chain.

How to inspect a chain#

When you suspect a chain problem, the definitive test is to look at exactly what the server presents, not what a browser papers over. The classic command is:

openssl s_client -connect example.com:443 -showcerts

That opens a live TLS connection on port 443 and prints every certificate the server actually sent, in order. Count them: a complete chain shows your leaf followed by one or more intermediates. If you see only the leaf, you've found your missing-intermediate bug. The certificate section lists each cert's subject and issuer, so you can walk the links by hand and confirm each issuer matches the subject of the next certificate up.

For a faster read on a certificate's identity, our SSL inspector pulls a domain's most recent certificate from the public Certificate Transparency logs and reports the leaf, the issuing authority, the validity window, hostname coverage, and the HSTS state, with an overall grade. One honest limitation is worth stating plainly, because it's exactly the kind of thing this article is about: because that tool reads from CT logs rather than performing a raw TLS handshake, it shows you which authority issued the certificate but cannot see the chain your server is actually serving on the wire. For diagnosing a missing intermediate specifically, reach for openssl s_client -showcerts or a full handshake analyzer. The CT-log view and the handshake view answer different questions, and knowing which one you need is half the battle.

Two related tools round out the picture. Every certificate ever issued for your domain is recorded in Certificate Transparency logs, which is how the inspector finds them in the first place. And if you want to control which authorities are even allowed to issue for your domain (and therefore which roots your chains can ever lead back to), a CAA record is the lever for that.

A short checklist#

If you're deploying or auditing a certificate, the chain-specific failures come down to a handful of checks:

  • Serve the full chain, not just the leaf. Point your server at the bundle that includes intermediates (fullchain.pem or your platform's equivalent), never the leaf-only file.
  • Test with a strict client, not just a browser. A quick curl https://yourdomain or an openssl s_client run will fail loudly on an incomplete chain that Chrome would have hidden.
  • Order matters: leaf first. The certificate your domain owns goes at the top, intermediates follow in ascending order toward the root.
  • Don't include the root. It's redundant and occasionally counterproductive; the client supplies it from its own trust store.
  • Mind old clients on new authorities. If you must support very old devices, check whether your issuer's modern root is present in those trust stores, or whether you're leaning on a cross-signed path that could one day expire.

The leaf gets all the attention because it carries your domain name and its expiry date. But a certificate that's flawless in isolation is still useless if the client can't connect it to a root it trusts. The chain is the part that does the connecting, and when HTTPS behaves differently depending on who's asking, the chain is the first place to look.

Try it

Inspect a certificate

Check any domain's current certificate — the issuing authority, expiry, hostname coverage, and HSTS state, with a plain grade from A+ to F — pulled straight from the Certificate Transparency logs.

Inspect a certificate →