The certificate chain of trust
A certificate chain is the path from the certificate a server presents up to a root certificate already in the client's trust store. Each certificate is signed by the one above it, and verification walks that path until it reaches an anchor it already trusts. The server must send its own certificate **and** every intermediate in between; it must not need to send the root, because a client that does not already have the root would gain nothing by being handed it. A chain missing an intermediate is the most common TLS misconfiguration there is.
What verification actually does #
When a client connects, the server sends a list of certificates. The client then walks upward:
- Take the server’s certificate. Read its issuer name.
- Find a certificate whose subject matches that issuer. Verify the signature with its key.
- Repeat, moving up one link at a time.
- Stop when you reach a certificate that is already in the local trust store.
If step 2 ever fails to find a match, verification stops with unable to get local issuer certificate.
Root CA (in your OS trust store — the server does not send this)
| signs
Intermediate CA (the server must send this)
| signs
example.com (the server's own certificate) Why intermediates exist at all #
Root CA private keys are enormously valuable and are kept offline, often in hardware in a vault. If a root signed certificates directly, that key would need to be online and in constant use.
So roots sign a small number of intermediates, and intermediates do the day-to-day issuing. A compromised intermediate can be revoked without touching the root — and without every operating system and browser on earth needing an update. See roots vs intermediates.
Building the chain file #
Leaf first, then upward. This is simple concatenation:
cat certificate.crt intermediate.crt > fullchain.crt Checking what a server sends #
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
| grep -c "BEGIN CERTIFICATE" Expected output
2
One certificate from a publicly trusted site means the intermediate is missing. To see the chain in detail:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| grep -E "Verify return code|^ *[0-9] s:" Expected output
0 s:CN = example.com 1 s:C = US, O = Example CA, CN = Example Intermediate CA Verify return code: 0 (ok)
Why browsers hide the problem #
Browsers do two things OpenSSL does not:
- Cache intermediates. Having seen an intermediate on another site, a browser reuses it.
- Follow AIA. The Authority Information Access extension carries a URL for the issuer’s certificate, and browsers will fetch it.
So a broken chain works in Chrome, on your machine, because you happened to visit another site using the same CA. It fails for a fresh browser profile, for curl, for your monitoring, and for the API client your customer wrote. The server is misconfigured in every case.
openssl x509 -in certificate.crt -noout -text | grep -A1 "Authority Information Access" Expected output
Authority Information Access: OCSP - URI:http://ocsp.example-ca.com CA Issuers - URI:http://crt.example-ca.com/intermediate.der
That file is normally DER — convert it to PEM before concatenating.
Verifying a chain you have on disk #
openssl verify -CAfile root-ca.crt -untrusted intermediate.crt certificate.crt Expected output
certificate.crt: OK
Roots go in -CAfile, intermediates in -untrusted. Putting an intermediate in -CAfile makes the
check pass for the wrong reason. See
verifying a certificate chain.
What breaks a chain #
| Problem | Symptom |
|---|---|
| Intermediate not sent | unable to get local issuer certificate |
| Certificates in the wrong order | Fails on strict clients, works on lenient ones |
| An intermediate has expired | Fails even though the leaf is valid for months |
| Root not in the client’s trust store | Fails on that client only — typical for private CAs |
| Wrong intermediate for the leaf | Signature verification fails partway up |
Frequently asked questions
- Should the root certificate be in the chain the server sends?
- No. A client that trusts the root already has it, so sending it wastes bytes on every handshake. A client that does not trust it will not start trusting it because the server said so. Send the leaf and the intermediates, and stop there.
- Why does a site work in Chrome but fail with curl?
- Browsers repair broken chains. They cache intermediates from previous sites and follow the Authority Information Access URL to fetch a missing one over HTTP. curl and OpenSSL do neither. The chain is genuinely broken in both cases — the browser just hides it.
- Does the order of certificates in the file matter?
- Yes. The leaf must come first, then each intermediate walking up toward the root. Some clients tolerate other orders, many do not, and a wrongly ordered file fails for a subset of users, which is unpleasant to diagnose.
- How many intermediates are normal?
- One or two. A chain of leaf plus one intermediate is the common case for a publicly trusted certificate; cross-signed setups sometimes add another.
References
- RFC 5280: X.509 and CRL profile — the specification every field on a certificate comes from
- RFC 4158: certification path building — why a verifier can fail to find a path that exists