TLS certificate chain errors mean a client cannot build a trusted path from your server's certificate to a root certificate authority, for example because the server is not sending its intermediate certificate. This guide gives the exact openssl commands to confirm it, four causes to check, the fix for each, and how to verify the fix worked. A mistake to avoid: testing only in Chrome, which quietly repairs some broken chains itself.
Contents
- What does a TLS certificate chain error actually mean?
- How do you confirm it is a chain error, not something else?
- Cause 1: the server is not sending its intermediate certificate
- Cause 2: the certificates are in the wrong order
- Cause 3: an old or replaced intermediate is still being served
- Cause 4: it only fails in some clients, not others
- How do you verify the fix worked?
- How do you stop it coming back?
- Start today
- Frequently asked questions
What does a TLS certificate chain error actually mean?
A TLS certificate chain error means the browser or client cannot build an unbroken path from the server's certificate up to a trusted root certificate authority, even when the leaf certificate itself is valid and unexpired.
You will usually spot it from what your browser or client shows:
- Chrome,
NET::ERR_CERT_AUTHORITY_INVALIDon its "Your connection is not private" warning page, when it cannot repair the chain itself - Firefox, an unknown-issuer warning instead of a padlock
- Command-line and library clients, such as curl, show
unable to get local issuer certificateorunable to verify the first certificate - OpenSSL, shows these as error 20 and error 21 (x509_vfy.h header; X509_STORE_CTX_get_error manual)
At least three certificates are involved in a publicly trusted chain: a self-signed root already trusted by the client, one or more intermediates between that root and the server's certificate (usually supplied by the server), and the leaf certificate.
This differs from an expired certificate, a hostname mismatch or a self-signed leaf: a chain error means a link is missing or broken between the leaf and a trusted root. The sections below cover common TLS certificate chain errors, with an exact fix for each.
How do you confirm it is a chain error, not something else?
You confirm a chain error by reading the certificates the server sends, not by how a browser displays the site. Run openssl s_client -connect example.com:443 -verify_hostname example.com -showcerts and check the BEGIN CERTIFICATE count and the final Verify return code line (openssl s_client manual).
- 0 (ok), the chain validated to a trusted root
- 20, unable to get local issuer certificate, an issuer certificate is missing
- 21, unable to verify the first certificate, the certificate received cannot chain to anything held
These are OpenSSL's verify error codes (X509_STORE_CTX_get_error manual; x509_vfy.h header); leave out -verify_return_error for this check, because it stops the handshake at the first error (openssl s_client manual) and, against a TLS 1.2 server, the final line can then read Verify return code: 0 (ok) although verification failed.
Chrome or Safari alone can hide the fault by repairing a missing intermediate themselves, and so can the curl built into Windows, which relies on Windows certificate checking; use openssl, or curl on Linux, for a true reading of what your server sends.
Rule out an expired certificate or hostname mismatch first with ShieldMarc's free SSL checker (issuer, SANs, expiry, TLS version), though it shows no chain detail and cannot say why an untrusted certificate fails, so use openssl to diagnose a broken SSL certificate chain.
Check the chain
Run openssl s_client -showcerts and read the verify return code.
Get the current bundle
Download today's certificate and intermediate from your CA, not a cached copy.
Order it correctly
Put your certificate first, then the intermediate, root last or omitted.
Redeploy and reload
Point the server at the combined file and reload, not restart, the service.
Verify from outside
Recheck with openssl or curl, using a client that does not self-repair chains.
Cause 1: the server is not sending its intermediate certificate
This is a common cause of a chain error, a missing intermediate certificate: the server has only its own leaf certificate and never received the one that signed it.
- Get the current chain, from the certificate authority's own page or your ACME client's output, not an old saved copy.
- Build one file in order, your certificate first, then every certificate in the chain your certificate authority supplies, commonly named
fullchain.pem; keep any cross-signed root in that chain, such as the Root YR certificate in Let's Encrypt's current RSA chains, which clients need to reach a root they already trust. - Point the server at the right file, the combined file in nginx's
ssl_certificatedirective or Apache'sSSLCertificateFile(2.4.8+), the intermediates alone inSSLCertificateChainFileon older Apache, or a load balancer's certificate and chain fields. - Reload, not restart, the server, then confirm with the checks later in this guide.
ACME clients such as Certbot (fullchain.pem) and acme.sh (the file named by --fullchain-file) already write the full chain, so point the server at that file rather than the certificate alone.
Cause 2: the certificates are in the wrong order
Here the certificates are present but out of order, usually from a hand-assembled file. RFC 9846 section 4.5.1 (TLS 1.3, replacing RFC 8446) puts your certificate first, then each certificate that signed the one above it; the root may be left out, as TLS 1.2 allowed under RFC 5246 section 7.4.2.
- Read every certificate's subject and issuer, with
openssl crl2pkcs7 -nocrl -certfile file.crt | openssl pkcs7 -print_certs -noout, sinceopenssl x509reads only the first certificate in a file. - Reorder the file, so each certificate's issuer matches the subject of the one below it.
- Rebuild
fullchain.pemin that order, and redeploy.
Extra certificates after the leaf are not a fault in themselves, as RFC 9846 says clients SHOULD cope with them and with any order after the leaf, so a misordered chain breaks only clients that do not; a cross-signed intermediate during a root transition is not a fault either, since either valid cross-sign can sit second (Let's Encrypt: certificates).
Pro Tip: When you rebuild fullchain.pem, list every certificate inside it with openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout before you deploy, since openssl x509 reads only the first one, and the issuer of each entry must exactly match the subject of the entry below it, or the reorder has not actually fixed anything.
Cause 3: an old or replaced intermediate is still being served
Certificate authorities rotate or retire intermediates, and a server serving last renewal's bundle breaks even though its leaf certificate is fine; occasionally an intermediate expires or is revoked.
- Check the intermediate's dates, with
openssl x509 -in intermediate.crt -noout -dates. - Fetch the current chain, from the certificate authority's own page, not a cached file.
- Rebuild
fullchain.pemand redeploy, to every server or load balancer node using it. - Check your renewal automation, that it re-fetches the chain each run and is still running, not silently failing.
Cause 4: it only fails in some clients, not others
A broken chain can load in one client and fail in another, because several clients repair a missing intermediate themselves.
- Chrome, on Windows, macOS, ChromeOS, Linux and Android, fetches a missing intermediate via AIA; on iOS it defers to Apple, whose SecTrust API downloads one too (Chrome Root Store FAQ; Chromium's certificate verifier; SecTrustSetNetworkFetchAllowed)
- Firefox, does not fetch one, closed as WONTFIX, and instead preloads disclosed intermediates (Bugzilla 399324; Mozilla: preloading intermediate CA certificates)
- curl on Linux, default Java and Android apps, do not repair one either: curl has no AIA fetching of its own, an open item on its to-do list; Java fetches one only when
com.sun.security.enableAIAcaIssuers=trueis set and, in current releases,com.sun.security.allowedAIALocationsallows the address, which it does not by default (Java PKI guide); Android's guidance says to serve the intermediate yourself
Treat "it loads fine in Chrome" as no evidence the chain is fixed: serve the complete chain yourself so curl, Java and mobile apps work too.
Self-repairs
- Chrome: fetches via AIA, not on iOS
- Safari/iOS: Apple's verifier fetches it
- Firefox: preloads common intermediates
Needs full chain
- curl: no built-in AIA support
- Java: AIA off by default
- Android apps: no automatic repair
How do you verify the fix worked?
You verify the fix by re-running openssl s_client without -verify_return_error and checking for Verification: OK and Verify return code: 0 (ok), with each certificate's issuer matching the subject below it.
Test the live server with a client that does not self-repair a chain, such as curl -v https://example.com on Linux, not the curl built into Windows, which relies on Windows certificate checking and so can fetch a missing intermediate itself; openssl verify -CAfile ca-bundle.pem -untrusted chain.pem yourcert.pem checks only your local files before you deploy, and needs the intermediates passed with -untrusted because openssl verify reads only the first certificate in a file (openssl verify manual).
Check from more than one edge location where a CDN is involved; ShieldMarc's free SSL checker then confirms the issuer, SANs, expiry and TLS version, though it shows no chain detail and its answer may be up to about an hour old.
How do you stop it coming back?
You stop a chain error coming back by making the fix part of your deploy pipeline, not a one-off manual rebuild. Build fullchain.pem from the certificate authority's current chain file as a pipeline step, so every renewal assembles it the same way.
Publicly trusted certificates are now capped at 200 days' validity, falling to 100 days from 15 March 2027 and 47 days from 15 March 2029 (CA/Browser Forum Ballot SC081v3), so renewals, and the chance to reintroduce a mistake, come round far more often; watch the certificate authority's own notices for root or intermediate changes.
Monitor continuously rather than waiting for a customer report: ShieldMarc's SSL monitor checks port 443 daily and alerts when a certificate is expired, invalid, or within 7 days of expiring, though it flags invalid rather than diagnosing why, so keep this guide's commands to hand alongside ShieldMarc's guide to SSL certificate monitoring.
For UK public sector readers, NCSC guidance recommends that government systems stop supporting TLS 1.0 and 1.1, deprecated by RFC 8996, and are upgraded to support TLS 1.3; fixing the chain is a good moment to check this too.
Start today
Run the openssl s_client command against your own domain; ShieldMarc's free SSL checker adds a quick read of issuer, SANs, expiry and TLS version with nothing to install, but it shows no chain detail. If broken, rebuild fullchain.pem in the correct order from the certificate authority's current files and redeploy: that fixes a missing, misordered or outdated intermediate, causes 1 to 3 above.
Add ongoing monitoring so the next renewal failure is caught before a customer reports it. ShieldMarc's Professional plan costs £70 a month, or £50 a month billed annually, and covers SSL monitoring across 25 domains; every sign-up starts with a 30-day trial, no card required.
ShieldMarc's checks cover port 443 web and API certificates only, not a mail server's STARTTLS handshake, so check an SMTP chain with openssl s_client -connect mail.example.com:25 -verify_hostname mail.example.com -starttls smtp -showcerts, since the certificate only arrives after the STARTTLS command. For the wider picture, ShieldMarc's free Security Grade check covers DNS, DMARC and certificates together as one A+ to F grade.
Frequently asked questions
Does a certificate chain error mean my certificate is invalid?
No: the leaf certificate can be valid and unexpired when this error appears. The fault is a broken link to a trusted root, from an intermediate that is missing, wrong, or out of order, so check expiry and chain completeness separately.
Why does the site work in Chrome but fail in curl or a mobile app?
Chrome's verifier, and Apple's, can fetch or already hold a missing intermediate, masking a broken chain. curl on Linux, many Java services and Android apps do not, so serve the complete chain yourself rather than rely on any client to patch it.
Do I need to include the root certificate in the chain?
No: clients already hold the root in their own trust store, so it is normally left out. The order needed is your certificate, then the intermediate or intermediates, including any cross-signed root the certificate authority supplies; sending the self-signed root too is simply unnecessary, not broken.
Does this affect email delivery too?
Sometimes: a mail server's STARTTLS certificate can have the same fault, and although opportunistic TLS between mail servers generally delivers past an untrusted certificate, a sender enforcing your MTA-STS policy must not deliver to it; ShieldMarc's checker and monitor test port 443 web certificates only, so run openssl s_client -connect mail.example.com:25 -verify_hostname mail.example.com -starttls smtp -showcerts instead. For Postfix, the order is server certificate first, then issuing CAs bottom-up, root optional unless DANE-TA (2 x x) TLSA records reference it (Postfix TLS README); see our MTA-STS and TLS-RPT guide for mail TLS.
How often should I recheck the certificate chain?
Recheck after every renewal, not just at initial setup, since intermediates rotate and automation can fail silently. Maximum certificate lifetimes are now down to 200 days, falling to 100 days from 15 March 2027 and 47 days from 15 March 2029 (CA/Browser Forum Ballot SC081v3), so renewals come round more often, and continuous monitoring catches a broken renewal in between.
Sources
- X509_STORE_CTX_get_error manual page (OpenSSL verify error codes)
- OpenSSL x509_vfy.h header (verify error numbers)
- openssl s_client manual page
- RFC 9846: The Transport Layer Security (TLS) Protocol Version 1.3
- OpenSSL verification options manual page
- RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2
- Let's Encrypt certificates (chains of trust)
- Chromium certificate verifier source (AIA fetching)
- Chrome Root Store FAQ (where the Chrome Certificate Verifier runs)
- SecTrustSetNetworkFetchAllowed (Apple Developer Documentation)
- Mozilla Bugzilla 399324: AIA fetching closed WONTFIX
- Preloading Intermediate CA Certificates into Firefox (Mozilla Security Blog)
- curl TODO list
- Java PKI Programmer's Guide
- Security with network protocols (Android Developers)
- openssl verify manual page
- CA/Browser Forum Ballot SC081v3 (certificate validity schedule)
- Using TLS to protect data (NCSC)
- Postfix TLS README
