· 5 min read

How to Read SSL Certificate Chains (And Why You Should)

Kief Studio
How to Read SSL Certificate Chains (And Why You Should)

How to Read SSL Certificate Chains (And Why You Should)

Your site works in Chrome on your laptop. The padlock is green. Everything looks fine.

Then a webhook fails. A mobile user gets a security warning. Your API integration throws SSL: CERTIFICATE_VERIFY_FAILED and you spend the next four hours learning what a certificate chain actually is.

This is the most common way developers discover that their TLS setup is broken. Not from a pentest. Not from monitoring. From a customer complaint.

The Padlock Lies

Here's the thing nobody tells you about browser TLS validation: desktop Chrome and Firefox are extremely forgiving. They cache intermediate certificates from previous sites you've visited. If your server sends an incomplete chain, your browser silently fills in the gaps from its local cache.

Your laptop says "secure." An iPhone seeing your site for the first time says "connection not private." Every curl call, every webhook, every API client that doesn't have a cached intermediate -- they all fail.

This is the number one reason chain misconfigurations go undetected for weeks. You tested it in the one environment designed to hide the problem.

What a Certificate Chain Actually Is

A certificate chain is a trust path. Your server's certificate (the "leaf") was signed by an intermediate CA, which was signed by a root CA. The root CA lives in your operating system's trust store. The chain connects them.

When a client connects, your server needs to send the leaf cert and every intermediate cert between it and the root. The root itself doesn't get sent -- the client already has it.

If you skip an intermediate, the client has to figure out the chain on its own. Desktop browsers try. Everything else gives up.

Here's what a healthy chain looks like:

Root CA (in client trust store)
  └── Intermediate CA (sent by server)
        └── Your certificate (sent by server)

And here's the broken version that passes desktop testing:

Root CA (in client trust store)
  └── Intermediate CA (NOT sent -- cached in browser, missing everywhere else)
        └── Your certificate (sent by server)

Same padlock. Completely different behavior depending on the client.

Reading a Certificate with OpenSSL

Stop guessing. OpenSSL will show you exactly what your server sends.

Connect to any site and dump the chain:

openssl s_client -connect example.com:443 -showcerts </dev/null 2>/dev/null

You'll get output like this (trimmed for readability):

CONNECTED(00000003)
---
Certificate chain
 0 s:CN = example.com
   i:C = US, O = Let's Encrypt, CN = R11
-----BEGIN CERTIFICATE-----
MIIFLzCCBBeg...
-----END CERTIFICATE-----
 1 s:C = US, O = Let's Encrypt, CN = R11
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
-----BEGIN CERTIFICATE-----
MIIFBjCCAu6g...
-----END CERTIFICATE-----
---

The s: line is the subject (who this cert belongs to). The i: line is the issuer (who signed it). If the chain is complete, each cert's subject matches the previous cert's issuer, all the way up to a root your system trusts.

If you only see certificate 0 and nothing else, your server isn't sending the intermediate. That's the problem.

Inspecting a Single Certificate

Once you have a cert file (or you grabbed one from the chain above), read every field:

openssl x509 -in cert.pem -text -noout

The output that matters:

Validity
    Not Before: Mar 15 00:00:00 2026 GMT
    Not After : May 14 23:59:59 2026 GMT
Issuer: C = US, O = Let's Encrypt, CN = R11
Subject: CN = example.com
X509v3 Subject Alternative Name:
    DNS:example.com, DNS:www.example.com

Check these three things every time:

  1. Not After -- is it expired or about to expire?
  2. Issuer -- does this match the next cert in your chain?
  3. Subject Alternative Name -- does it actually cover your domain?

That third one catches people more than you'd expect. Wildcard certs for *.example.com don't cover example.com itself (only subdomains), and a cert for www.example.com won't work on the bare domain.

Validating the Chain Locally

If you have your cert files on disk, verify the chain without making a network call:

openssl verify -CAfile chain.pem server.pem

chain.pem should contain your intermediate(s) and root. server.pem is your leaf cert. If it prints server.pem: OK, the chain is valid. If not, OpenSSL tells you exactly where it breaks.

For a quick check of a live server (does the chain validate against your system's trust store):

openssl s_client -connect example.com:443 </dev/null 2>/dev/null | head -20

Look for Verify return code: 0 (ok) at the bottom. Anything else means the chain doesn't validate.

Why This Matters More Than It Used To

The CA/Browser Forum passed Ballot SC-081v3 on April 11, 2025. Unanimous approval from Apple, Google, Microsoft, and Mozilla. Here's the timeline:

  • March 15, 2026: Maximum certificate lifespan drops to 200 days
  • March 2027: Down to 100 days
  • March 2029: 47 days

That first deadline already hit. If you renewed a cert after March 15, 2026, you're already on the shorter lifespan.

47-day certs mean you're renewing roughly every month and a half. If you're doing that manually, you're going to miss one. 72% of organizations had at least one certificate outage last year, and 37.5% of those were caused by expired certs.

The cost isn't abstract. When Starlink went down in 2024, it was a single expired certificate on a ground station. Not a hack. Not a DDoS. One cert nobody was watching. Microsoft Teams locked out 20 million users in 2020 for the same reason -- an expired auth certificate that their own management tools missed.

The Real Reason Certs Are Getting Shorter

Here's the hot take: 47-day lifespans aren't really about "better security" in the way most articles frame it. They're an admission that certificate revocation is broken.

CRLs (Certificate Revocation Lists) and OCSP (Online Certificate Status Protocol) are the mechanisms for invalidating a compromised cert before it expires. In theory, you revoke the cert, and clients check the revocation status before trusting it.

In practice, browsers routinely skip OCSP checks. Chrome hasn't done online OCSP lookups in years. Firefox soft-fails (if the OCSP server is down, it trusts the cert anyway). The whole revocation infrastructure is unreliable.

So the CA/Browser Forum's solution: make certs expire so fast that revocation barely matters. If a cert is compromised, it's only valid for a few more weeks anyway. It's pragmatic. It's also going to break a lot of manual renewal workflows.

What You Should Actually Do

Automate renewal. ACME protocol (the thing Let's Encrypt uses) works with most CAs now, including ZeroSSL and Google Trust Services. Certbot, acme.sh, or your cloud provider's built-in certificate manager. Pick one and set it up. 51% of organizations already named automated certificate lifecycle management their top priority for 2025.

Monitor your certs. Cloudflare offers free Certificate Transparency monitoring -- you get an email any time a certificate is issued for your domain. Cert Spotter from SSLMate does the same thing. Both catch unauthorized issuance (phishing domains using certs that look like yours) with zero setup.

Test from the right place. Don't just check your site in Chrome. Use SSL Labs Server Test for a full analysis. Or run the openssl s_client commands above from a server that has never visited your site before. If you want a quick check without the terminal, we built an SSL Checker on kief.dev that hits your server directly -- no browser caching to hide problems.

Watch the DigiCert ICA transition. DigiCert is revoking their G2 and G3 intermediate CAs on May 15, 2026. Not because they're compromised, but because Chrome and Mozilla now require dedicated TLS-only root hierarchies. If your cert chains through one of those intermediates, it'll show as untrusted after the revocation. Check your chain now and reissue if needed.

The One Command to Remember

If you take nothing else from this post, remember this:

openssl s_client -connect yoursite.com:443 -showcerts </dev/null 2>/dev/null

Run it. Count the certificates. If you only see one, your chain is incomplete and you're relying on browser caching to hide the problem. Fix it before your next webhook integration discovers it for you.

meta_title: "How to Read SSL Certificate Chains"
meta_description: "Your SSL looks fine in Chrome but breaks on mobile and API clients. Learn to read certificate chains with OpenSSL and catch misconfigurations before they cause outages."
tags: ["ssl", "tls", "certificates", "openssl", "security"]