SSL certificate chain checker.
Works in Chrome, fails in curl? This tool connects once, captures the certificate chain exactly as your server sends it, and shows which clients will accept it and which will refuse: browsers, curl, Python, Java, Node, Go, Android. If an intermediate is missing it fetches it the way a browser would, tells you who will not, and hands you the corrected fullchain.pem.
Connected to 104.154.89.105:443 as wrong.host.badssl.com · 3 certificates served JSON · text
Who will this break? modelled from the served chain
| Verdict | Client | Why |
|---|---|---|
| Browsers | ||
| fail | Chrome (desktop and Android) | The certificate does not cover wrong.host.badssl.com.NET::ERR_CERT_COMMON_NAME_INVALID |
| fail | Firefox | The certificate does not cover wrong.host.badssl.com.SSL_ERROR_BAD_CERT_DOMAIN |
| fail | Safari (macOS and iOS) | The certificate does not cover wrong.host.badssl.com.errSSLHostNameMismatch |
| fail | Windows (Edge, .NET, PowerShell) | The certificate does not cover wrong.host.badssl.com.CERT_E_CN_NO_MATCH |
| Command line | ||
| fail | curl and wget (OpenSSL) | The certificate does not cover wrong.host.badssl.com.SSL: no alternative certificate subject name matches target host name |
| fail | openssl s_client | The certificate does not cover wrong.host.badssl.com.verify error:num=62:hostname mismatch |
| Languages | ||
| fail | Python (requests, urllib3, certifi) | The certificate does not cover wrong.host.badssl.com.[SSL: CERTIFICATE_VERIFY_FAILED] Hostname mismatch, certificate is not valid |
| fail | Node.js | The certificate does not cover wrong.host.badssl.com.ERR_TLS_CERT_ALTNAME_INVALID |
| fail | Go (net/http) | The certificate does not cover wrong.host.badssl.com.x509: certificate is valid for a.example, not b.example |
| fail | Java (JDK 11, 17, 21) | The certificate does not cover wrong.host.badssl.com.No subject alternative DNS name matching example.com found |
| fail | Ruby, PHP and Perl (OpenSSL) | The certificate does not cover wrong.host.badssl.com.hostname does not match the server certificate |
| Mobile and devices | ||
| fail | Android apps (7.0 and later) | The certificate does not cover wrong.host.badssl.com.javax.net.ssl.SSLPeerUnverifiedException: Hostname not verified |
| fail | Docker and Alpine containers | The certificate does not cover wrong.host.badssl.com.hostname mismatch |
* passes, but for a reason the server did not earn. Verdicts come from the served chain, the Mozilla root store, 121 roots, as of 13 August 2026, and how each client is known to behave; nothing is executed in a browser or a JVM. Client root stores differ at the edges (see the reference table below).
Findings
-
pass
Leaf certificate 33 days left
Within its validity period.
-
pass
Chain completeness 2 intermediates served
The server sends every intermediate a strict client needs. The chain builds to a root in the Mozilla store without fetching anything.
-
pass
Chain order leaf first
Leaf, then each issuer in turn.
-
fail
Hostname does not cover wrong.host.badssl.com
Every client fails on this, whatever the chain looks like. Usually the server's default certificate is being served because SNI routing or the virtual host does not match. The SANs on the leaf show what it is actually for.
What the server sent in the order it sent it
-
leaf
*.badssl.com
33d left -
intermediate
YR2 (Let's Encrypt)
710d left -
intermediate
Root YR (ISRG)
2171d left
Path a browser builds: *.badssl.com › YR2 (Let's Encrypt) › Root YR (ISRG) › ISRG Root X1 (Internet Security Research Group)
The fix nothing to change in the chain
The chain is complete and in order. If a client still fails against this host, the problem is on the client: an old root store (Node, certifi or Java that has not been updated), a container without ca-certificates, or a corporate proxy re-signing traffic. How to tell which.
-----BEGIN CERTIFICATE----- MIIFADCCA+igAwIBAgISBlvhezWdMPyllFn5iTIxwdh9MA0GCSqGSIb3DQEBCwUA MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD EwNZUjIwHhcNMjYwNzI4MjAwMzAyWhcNMjYxMDI2MjAwMzAxWjAXMRUwEwYDVQQD DAwqLmJhZHNzbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF RHWjXdE2IKdWdu0+KZpDEG7ad6P4dljFTxUo5z5aO4zaqmtZP9uBrkInmQI8COK8 WAzpkldooMz53Gviymk24FslWxZGfUymwjNhKkQixww+DdGThLApoJKTtQijuddo ey3pqjEKwCtevKy26IIUzW0fPMXrl0PHSYglJMpCONHp3N3SieShf21/IYXE7yBp 7cwzzdGKCk20K6yKqs6rhigaABUySOCIoAJQ5XeXQLlh3H5tJ4TkU/lyw3StAdgl kCOKVmGINhFtGHL3jr/q5ubmDG11k2uxl03JgfJH6SU/C0mJO/+qWC+BU56qmA2R Xx8jeekwxvPXEVPyzcqvAgMBAAGjggIoMIICJDAOBgNVHQ8BAf8EBAMCBaAwEwYD VR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUdq8D8ru6 IYm7s2jqVCgw6zmnFnUwHwYDVR0jBBgwFoAUQBUtJnntMiCe35pyHdYyH4EMgQww MwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzAChhdodHRwOi8veXIyLmkubGVuY3Iu b3JnLzAjBgNVHREEHDAaggwqLmJhZHNzbC5jb22CCmJhZHNzbC5jb20wEwYDVR0g BAwwCjAIBgZngQwBAgEwLwYDVR0fBCgwJjAkoCKgIIYeaHR0cDovL3lyMi5jLmxl bmNyLm9yZy8xMjYuY3JsMIIBDQYKKwYBBAHWeQIEAgSB/gSB+wD5AHYAlE5Dh/rs we+B8xkkJqgYZQHH0184AgE/cmd9VTcuGdgAAAGfqohgFAAABAMARzBFAiBXseav 2NCAZB5EMinqevH4UVMLmdklRBAGJ3E+qyE+NAIhAN5cJ5MHfXHk+XLNB3Rmnst1 v+hzfR/8lnE40FiTG74uAH8AbP5QGUOoXqkWvFLRM+TcyR7xQRx9JYQg0XOAnhgY 6zoAAAGfqohjwQAIAAAFABgjq+UEAwBIMEYCIQCkFMBNvc7IZKPP0LtfFnfvf9UU NeJGlP7OnH/qyaXHqAIhANbj30XMrPLmiTnxKq0EIneGF3jeexb90azDwFreJkda MA0GCSqGSIb3DQEBCwUAA4IBAQAp0AlNyUO4JoL2MrfSaRCU8qfVXusdudgXUSGW LEKkv6j9XhDbDHKdfOTjfZif0uE7qiujXuNi5tIIfeJelyJ9Dl5Em2kSIdDM7JFu N3I7Hd5iDZrG6/fVvP+ID8lUb2e3sBG7OMifMwNkGHAy1u+UNkiO4u+IBCjl0mvI aCRAhD6Mh3r8/0E5mwlr8xlYw5RhuInC19T043iIftDbeIN85Zp6Z+MugdvXnIUT sH0eFFUMbfTqph4V9Qwn0bCxY2mdoP7U3pjDpCI9rSr2Oga64KHA0ejGDbi9KoQN CWbg38/by9x0S4fwXWaXTqU09r80qaIBUM1MCY89LA5nGPny -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIE2jCCAsKgAwIBAgIQTr0klH4k05SALYSlL9WzGTANBgkqhkiG9w0BAQsFADAu MQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZUjAe Fw0yNTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYw FAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZUjIwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQDZ0LxwBppqh84luqMerV/eeL/fXQ7mLQQv1Lnp WKZbyvGpx6wh6AfnslAnF6ewTkcHA+gSOoBvm3Dfm06AuGiF+KRut4fAcowqnAQQ CW98+QPP/eOv/wug7Iyk4NkOxf2I6g2f55T6nJoOTLFcukeRq80JGQEYan+dPFr9 OGUgQK2hGKgNkW87pappsOAuUJcroYhRt5uUis4qaZireiseu32gzDJNBAiKtsvd 6HX4v25bpkRNcS/B/Gtc9kVbUpD+2PLPxdei3Tim55k4tfAEXwD2qyiPTxrTNq6l N+AMr5g2c1dNqkOTwjxeV6L5lpP1rGiYvLnRaPlOqyZRPW+5AgMBAAGjge4wgesw DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMBMBIGA1UdEwEB/wQI MAYBAf8CAQAwHQYDVR0OBBYEFEAVLSZ57TIgnt+ach3WMh+BDIEMMB8GA1UdIwQY MBaAFN7nW2DQIm1AKH0/DQH+pLVStFGUMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEF BQcwAoYWaHR0cDovL3lyLmkubGVuY3Iub3JnLzATBgNVHSAEDDAKMAgGBmeBDAEC ATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veXIuYy5sZW5jci5vcmcvMA0GCSqG SIb3DQEBCwUAA4ICAQB0ZUQWZ9/Yn9COEpo+JfecMnB0h0vwDm/M66IqXqw3LoaL mx9lZvRTeDIS67PUeI3yCA2W6PKRD0/FE/G57lOmS+Xy5AaaL00ICGOqjNcCaMWW 8o8nevHOd4i4lqgtznE/28QwlcdJyF8yBiWHpnyjhEpmNWJURgOCOg2xpwRMBCsj MScqYPtOhBeuYQvSwAEeTML2Ukh6uGuX4E14q65Ja8cdjF5bAldnP1eE4FBaAwsZ G2fOqqrKV03Y85Nw2btedP1AtliQuJZs/Jo/gXxXdc7LrH3McgnpnbTiAncX7yES hP6kzQejllqMCIt52HOjxDGWafS7Xw+DKwqmH+Eqy8dcbOuag/1AYlQoKNVK3F5q Hh6tEDiMqQcLIibGKteE6iHo4A/bIScbzrhXUYuism42ZYzmc48FMVIH3qy4L84E TdAH2gtxw0PAhvRVXp8HP7wfngpzsN/8xOTpeRSbM4+Qbc56G6+Bifmv6sk1ieQb NA3wJdl4DDUuQSV8hBgx6zoI1ZSGORprDFux7c6rhc77QZMSRrEgomBeklervEve 86ylWmZ3WWHV6RLMi8xNvjd71r4EPIGgY7BZU/VPBkq+uA7Gb6mbJnFgV43uh3xy LRFgxIAphIukwTGSMZZR+AI+Qnp0BYTWovHXozOf3H8r6hozEoT02JHn0AeTfA== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIF9DCCA9ygAwIBAgIRAPJLbRf52a18scn+p4eCaZ8wDQYJKoZIhvcNAQELBQAw TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjYwNTEzMDAwMDAw WhcNMzIwOTAyMjM1OTU5WjAuMQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQ MA4GA1UEAxMHUm9vdCBZUjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB ANvGJnN78CTJdWL3+eGfsLN5TrNBJs+VH9hRXqRbwxu9sGNiB0BD1fcOxbSUQCJI M1xE13Db+5Cw1w0s0EBYsvuIP/6joF0w8cuImbgR1OGgYbSQ4OpzI+DG8SGuTlcE 873OCS+kh3srlo6vl43M5OJg4Aeo1sfHp6kTJDoIiFBNJAY+OKfX/FUvYKuhjT+n o49lmqmupSBI5PkBQiqrEGtWU5uxU/cQWHGu8jSjFBznZqvbNPLMXMLFxCb3WTfr JBXXjqvWG+v4bjzxjjeAtOlU7qarRDvNOyAuQYLln904M+faKx8hnLCpJ15ZqaEg cNlY+9MMWcC5yvL2A2j3l9+2buggZX+dOE91zYmIdawTvSZuVvlbRrAlLxIB6pwM BjneXCjYQ8+3BCCjssbSNpZU3hTcBDdhfAlEDlYr6pEatnMdmDT5BqnKC92bd0Eh M1fbLHioLccLCuievT8ZkPhZrq7Mii7gNXAcUEAR8+lzYal+9zTg7C5DALyVOeG/ CqfRAMn1KSHCR0NSA6P8tn/mGRlnCct5rtVCLnVySVpU6H1qGg3DgTOuskf8eahT MiYbI5ezPJmO5ertalskQ1utp74+eDy92PI4ftHKTbq9IWhH4YZKh3WnJEIt+oQv lYZbY8tpEroKrFB6PFGzrJIDRyts4HqvuH52RFj2zv/BAgMBAAGjgeswgegwDgYD VR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMB Af8wHQYDVR0OBBYEFN7nW2DQIm1AKH0/DQH+pLVStFGUMB8GA1UdIwQYMBaAFHm0 WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYW aHR0cDovL3gxLmkubGVuY3Iub3JnLzATBgNVHSAEDDAKMAgGBmeBDAECATAnBgNV HR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5jci5vcmcvMA0GCSqGSIb3DQEB CwUAA4ICAQA8spSI95KKfn2W6GMmDpHBJSPaLbsS3W93cijJCRCYAc1fsJgL1FIL 7C0C9ecPOdcwB2fi0Dk2p94j9iTJCxmt5CFSKLRWwnXT2MMSXexVxqoVB79BdWPx VXETkVme/qYSAuKVHh5Ps+5BixgmwS1JkjSAc+MfrUbNssVEEnH0aEiAh+rotXAV JSP/Ye7LJPEwD9DWG72vVWbhAcuOf5OLjz57Ctk7MgQHynZ7+PlHJtajroCaIbtC r6tcZZaAwUQm+jQyeWdV+2hv9deOYFmKeQyjjcSrN5Nadrw+L9DZJLbA1HqeNvLh BgqpP0fvJq2N6EtD574N6eMI7uMsJTnji2UDz9el5XLSv9fqJMuDQtYVb2oTNoKp oUqhxPVC0aq4eG5MESaIdn8b5ZGSSeAJLMHXljEdlNza+ncfkviXk1POLnnFdvx8 /gk6M374WbLWFXw8N141B/Rl/tINGfl1TxOIiqtiMYkL02RSGb1kq34BL9NPP27z RGMuHGnzS3hFIrRTfKxrzUZ9RzQWzEG3K6fJ3r2nqSltkeytis9DIBoFY9VmVyjL M71DMi+y1+TRSJVClEMwvA4yL++7q9XZx5r5wBRWB4kQTKH5qyoZnDw7iiuh1lID yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA== -----END CERTIFICATE-----
Reproduce what this page saw, from any machine with OpenSSL:
openssl s_client -connect wrong.host.badssl.com:443 -servername wrong.host.badssl.com -showcerts </dev/null 2>/dev/null | grep -c 'BEGIN CERT' # 1 = leaf only, 2 or more = leaf plus intermediates curl -vI https://wrong.host.badssl.com 2>&1 | grep -iE 'issuer|SSL certificate problem'
Server configuration for the file above is in how to build a correct fullchain.pem.
About this tool
This server opens one TLS connection to the host and port you give it, with the SNI name you give (the host by default), and keeps the certificates exactly as the server sent them, in the order it sent them. It then does what a strict client does: tries to build a path from the leaf to a root in the Mozilla root store, 121 roots, as of 13 August 2026 using only what was served. If that fails it does what Chrome does instead: follows the Authority Information Access URL in the leaf to fetch the missing issuer, and tries again. The difference between those two results is the whole "works in Chrome, fails in curl" problem, and the verdict matrix is that difference spelled out per client.
The verdicts are modelled, not executed. No browser, JVM or Python interpreter runs on this server. Each row is a documented behaviour: whether the client fetches intermediates, whether it has public intermediates preloaded, whether it requires a Subject Alternative Name, and what it prints when it fails. Root stores differ slightly between clients (Java's cacerts, Node's compiled-in bundle and Android's store all lag Mozilla by months to years), so a chain that ends in a very new root can fail on one of them and pass here; the table below says where to look.
What the chain checker reports
| Check | Why it matters |
|---|---|
| Chain completeness | Whether the served certificates alone reach a trusted root. If they do not, and fetching the issuer over AIA makes them, the chain is incomplete: browsers recover, most other clients fail with unable to get local issuer certificate. This is the most common real-world certificate fault. |
| Client verdicts | Pass, pass with a caveat, or fail for 13 client families, each with the reason and the exact error string that client prints, so you can match it to the one in your logs. |
| Missing intermediates | The certificate the server should be sending and is not, fetched from the CA so you can see it and so the fullchain below is complete. |
| Order and extras | Whether the leaf comes first and each issuer follows, whether the root is being sent (wasted bytes), and whether a certificate in the bundle signs nothing (usually a leftover from a previous CA). |
| Expiry | Of the leaf and of every intermediate. An expired intermediate takes every site signed by it down at once, and monitoring that watches only the leaf will not see it coming. |
| Hostname and SAN | Whether the certificate covers the name you asked for, and whether it has a Subject Alternative Name at all. CN-only certificates are refused by Chrome, Firefox, Safari, Go and Python 3. |
| Signature and key | SHA-1 signatures and RSA under 2048 bits, both rejected by current clients. |
| The fix | A fullchain.pem built from the correct path, leaf first, root omitted, ready to paste, plus the OpenSSL command to see what this page saw. |
The clients in the matrix
Each row of the verdict table is one of these behaviours. "Fetches" means the client follows the AIA URL in a leaf to download a missing intermediate. "Preloads" means it ships every publicly disclosed intermediate and so rarely needs to. The error column is what you will find in that client's logs when the chain is incomplete.
| Client | Fetches | Error on a missing intermediate | Notes |
|---|---|---|---|
| Chrome (desktop and Android) | yes | NET::ERR_CERT_AUTHORITY_INVALID | Fetches missing intermediates from the AIA URL in the leaf, and caches ones it has seen. Uses the Chrome Root Store, which tracks Mozilla's closely. |
| Firefox | preloads | SEC_ERROR_UNKNOWN_ISSUER | Does not fetch over AIA, but preloads every intermediate disclosed to Mozilla's CA programme, so a missing public intermediate is usually papered over. |
| Safari (macOS and iOS) | yes | This Connection Is Not Private (errSSLXCertChainInvalid) | Apple's verifier fetches missing intermediates. iOS 13 and later also require a SAN and refuse leaves valid for more than 825 days. |
| Windows (Edge, .NET, PowerShell) | yes | CERT_E_CHAINING / PartialChain | Schannel fetches missing intermediates and downloads roots on demand from Windows Update, so it is the most forgiving client here. |
| curl and wget (OpenSSL) | no | SSL certificate problem: unable to get local issuer certificate | Reads the distribution's ca-certificates bundle (Mozilla's roots) and never fetches intermediates. This is the client most people discover the problem with. |
| openssl s_client | no | verify error:num=20:unable to get local issuer certificate | The reference view of what came over the wire. -showcerts prints exactly the certificates the server sent. |
| Python (requests, urllib3, certifi) | no | [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate | requests uses the certifi bundle, which is Mozilla's roots as of whenever certifi was last upgraded. No AIA fetching. Python 3.7 and later ignore the CN entirely. |
| Node.js | no | UNABLE_TO_VERIFY_LEAF_SIGNATURE: unable to verify the first certificate | Compiles Mozilla's roots into the binary rather than reading the system store, so an old Node release is an old root store. No AIA fetching. |
| Go (net/http) | no | x509: certificate signed by unknown authority | Uses the system store on Linux (so an Alpine image needs ca-certificates installed). No AIA fetching. Refuses CN-only certificates since Go 1.15. |
| Java (JDK 11, 17, 21) | no | PKIX path building failed: unable to find valid certification path to requested target | Uses the JDK's own cacerts file, not the operating system's. AIA fetching exists but is off unless com.sun.security.enableAIAcaIssuers=true is set. |
| Ruby, PHP and Perl (OpenSSL) | no | certificate verify failed (unable to get local issuer certificate) | All three wrap OpenSSL and behave like curl: system roots, no AIA. |
| Android apps (7.0 and later) | no | javax.net.ssl.SSLHandshakeException: Trust anchor for certification path not found | Conscrypt never fetches intermediates, and apps do not share Chrome's cache. Android 7.1.1 and older additionally lack ISRG Root X1 (Let's Encrypt). |
| Docker and Alpine containers | no | unable to get local issuer certificate | Whatever runs inside behaves like curl or Go above. If the image has no ca-certificates package at all, every HTTPS connection fails whatever the server sends; that one is fixed in the Dockerfile, not on the server. |
Why a certificate works in Chrome but fails in curl
A public certificate is not trusted on its own. The client has to build a path from your leaf, through one or more intermediates, to a root it already has. The server is supposed to send the leaf and every intermediate; the client supplies the root. When the server sends only the leaf, a strict client has a leaf signed by something it has never seen, and stops there.
Browsers are not strict. Chrome, Safari and Windows read the AIA URL embedded in the leaf and download the missing intermediate on the fly. Firefox has every public intermediate preloaded. All of them also cache intermediates from other sites, and Let's Encrypt's are on half the web. So the person who deployed the certificate opens the site, sees a padlock, and ships. Then a webhook sender, a mobile app, a CI job or a monitoring agent connects, and every one of them fails, because none of them fetch. The longer version is why your certificate works in Chrome but fails in curl.
How to build a correct fullchain.pem
Every server wants the same thing: the leaf first, then each intermediate in turn, in one PEM file, and no root. The result panel above produces exactly that from the correct path. Certbot writes it as fullchain.pem next to cert.pem; the mistake is pointing the server at cert.pem. Commercial CAs send it as a separate ca-bundle.crt or intermediate.crt; the mistake is not concatenating it. The step-by-step version, including how to find the right intermediate when the CA's download page is unhelpful, is how to build a correct fullchain.pem.
# nginx ssl_certificate /etc/ssl/site/fullchain.pem; ssl_certificate_key /etc/ssl/site/privkey.pem; # Apache 2.4.8 and later (SSLCertificateChainFile is deprecated) SSLCertificateFile /etc/ssl/site/fullchain.pem SSLCertificateKeyFile /etc/ssl/site/privkey.pem # HAProxy wants the key in the same file cat fullchain.pem privkey.pem > /etc/haproxy/certs/site.pem bind :443 ssl crt /etc/haproxy/certs/site.pem # Check the file before you reload: prints one subject/issuer pair per certificate openssl crl2pkcs7 -nocrl -certfile fullchain.pem | openssl pkcs7 -print_certs -noout
Cloud load balancers (AWS ALB and CloudFront, Google, Azure Front Door) have a separate "certificate chain" field when you upload your own certificate. It is optional in the form and mandatory in practice: leave it empty and you have shipped a leaf-only chain to every non-browser client.
Common chain problems
- Leaf only: the certificate was installed without the intermediate. Browsers pass, everything else fails. Serve the fullchain.
- Expired intermediate: the leaf was renewed, the bundle was not. Every client fails, including browsers unless they can fetch a current one. Download the CA's current intermediate and redeploy.
- Wrong intermediate: a bundle from the previous CA is still being served with a leaf from the new one. Shows here as an unrelated certificate plus an incomplete chain.
- Root sent: harmless but wasted bytes on every handshake. Remove it from the file.
- One edge out of four: a renewal that reached three load balancers and not the fourth. Check each edge by putting its IP in Host and the site name in SNI.
- Client-side, not server-side: the chain is complete here but a client still fails. That client has an old root store, no
ca-certificatespackage, or a proxy in the way. See unable to get local issuer certificate for how to tell.
Examples
- incomplete-chain.badssl.com: the missing intermediate, recovered over AIA, browsers pass and the rest fail
- example.com: a complete chain that passes everywhere
- self-signed.badssl.com: an untrusted root
- expired.badssl.com: an expired leaf
- wrong.host.badssl.com: a hostname mismatch
- smtp.gmail.com:465: a mail server's chain
From the command line
curl "kirkdiamond.com/tools/chain?host=example.com" gives the verdict matrix and findings as plain text, with the corrected fullchain appended when the served one is incomplete; add &format=json for structured output including fullchain_pem, and &port= and &sni= as needed. Only public hosts can be checked, the AIA fetch goes through the same guard, and nothing about the connection is stored.