Updated April 2026

How to Check Your SSL Certificate Chain

Quick Answer

A complete SSL chain has three parts: your domain certificate (leaf), an intermediate CA certificate, and a root CA certificate. Missing the intermediate is the most common SSL misconfiguration — desktop browsers cache it and work fine, but mobile browsers and API clients fail. Check with openssl s_client -connect domain.com:443 -showcerts.

The most painful SSL bug: you renew your certificate, desktop Chrome works, you close the ticket — three hours later your mobile team reports all API calls are failing. Missing intermediate certificate. Desktop cached it. Mobile didn't.

Visualize your SSL chain →

The three parts of an SSL trust chain

PartWhat it isWhere it lives
Leaf certificateYour domain's certificateYour server sends it
Intermediate CAIssued by root, issues your certYour server must send it
Root CAPre-installed in browsersAlready in the trust store

Browsers build the trust path: Leaf → Intermediate → Root. If the intermediate is missing, the browser can't verify your certificate and shows an error — unless it has the intermediate cached from a previous connection.

Check your chain with openssl

# Show the full certificate chain your server sends
openssl s_client -connect example.com:443 -showcerts 2>/dev/null | grep "s:"

# Output should show 2-3 lines like:
# s:CN = example.com ← your cert
# s:C = US, O = Let's Encrypt, CN = R3  ← intermediate
# s:O = Internet Security Research Group ← root (sometimes omitted)
# Check with curl (simulates API client behavior)
curl -sI https://example.com
# If this works but mobile fails, you have a chain issue

Fix: Nginx — use fullchain.pem not cert.pem

The most common cause of missing intermediate with Let's Encrypt:

# Wrong — leaf cert only
ssl_certificate /etc/letsencrypt/live/example.com/cert.pem;

# Correct — fullchain.pem includes leaf + intermediate
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# After fixing, reload:
nginx -t && systemctl reload nginx

Fix: Apache

# Apache 2.4.8+ — use fullchain.pem
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

# Older Apache — specify chain file separately
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem

Fix: Build fullchain manually

If you have separate cert files from a commercial CA:

# Combine leaf + intermediate into fullchain
cat your_domain.crt intermediate.crt > fullchain.crt

# Verify the chain
openssl verify -CAfile root.crt -untrusted intermediate.crt your_domain.crt

Certificate expiry — check proactively

# Check expiry date
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

# Output:
# notBefore=Jan  1 00:00:00 2026 GMT
# notAfter=Apr  1 00:00:00 2026 GMT  ← this is the date that matters

Let's Encrypt auto-renewal

# Check if auto-renewal is configured
systemctl status certbot.timer

# Test renewal without actually renewing
certbot renew --dry-run

# Force renewal if less than 30 days left
certbot renew --force-renewal
nginx -t && systemctl reload nginx
Visualize your SSL chain →