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.20.23.154:443 as example.com · 4 certificates served JSON · text

Verdict All 13 clients accept this chain 13 pass · 0 with a caveat · 0 fail
Chain Complete Every intermediate is served
Hostname Matches example.com
Leaf expires 33 days 2026-10-27T22:17:21Z

Who will this break? modelled from the served chain

VerdictClientWhy
Browsers
pass Chrome (desktop and Android) Builds and verifies the chain from what the server sends.
pass Firefox Builds and verifies the chain from what the server sends.
pass Safari (macOS and iOS) Builds and verifies the chain from what the server sends.
pass Windows (Edge, .NET, PowerShell) Builds and verifies the chain from what the server sends.
Command line
pass curl and wget (OpenSSL) Builds and verifies the chain from what the server sends.
pass openssl s_client Builds and verifies the chain from what the server sends.
Languages
pass Python (requests, urllib3, certifi) Builds and verifies the chain from what the server sends.
pass Node.js Builds and verifies the chain from what the server sends.
pass Go (net/http) Builds and verifies the chain from what the server sends.
pass Java (JDK 11, 17, 21) Builds and verifies the chain from what the server sends.
pass Ruby, PHP and Perl (OpenSSL) Builds and verifies the chain from what the server sends.
Mobile and devices
pass Android apps (7.0 and later) Builds and verifies the chain from what the server sends.
pass Docker and Alpine containers Builds and verifies the chain from what the server sends.

* 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 3 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.

  • pass
    Hostname example.com

    The certificate covers this name.

What the server sent in the order it sent it

  1. leaf

    example.com

    33d left
    Issuer
    Cloudflare TLS Issuing ECC CA 3 (SSL Corporation)
    Valid
    2026-07-29T22:10:08Z to 2026-10-27T22:17:21Z
    Key
    ECDSA P-256
    Signature
    ECDSA-SHA256
    SHA-256
    6153A96FD1A6AB7F4D438FC34932484299D0729D9140B3A126BB2F9C07B02200
    SANs
    • example.com
    • *.example.com
  2. intermediate

    Cloudflare TLS Issuing ECC CA 3 (SSL Corporation)

    3167d left
    Issuer
    SSL.com TLS Transit ECC CA R2 (SSL Corporation)
    Valid
    2025-05-29T19:49:45Z to 2035-05-27T19:49:44Z
    Key
    ECDSA P-256
    Signature
    ECDSA-SHA384
    SHA-256
    F15F29ABEF73AA4DD9AB754BAEAE3685BDD3874B46B525071177628685718026
  3. intermediate

    SSL.com TLS Transit ECC CA R2 (SSL Corporation)

    4040d left
    Issuer
    SSL.com TLS ECC Root CA 2022 (SSL Corporation)
    Valid
    2022-10-21T17:02:23Z to 2037-10-17T17:02:22Z
    Key
    ECDSA P-384
    Signature
    ECDSA-SHA384
    SHA-256
    5D1BC399274E649E1C72697DE91A54AD725088C5221CB61E17EE9C290BC42A92
  4. intermediate

    SSL.com TLS ECC Root CA 2022 (SSL Corporation)

    829d left
    Issuer
    AAA Certificate Services (Comodo CA Limited)
    Valid
    2025-08-01T00:00:00Z to 2028-12-31T23:59:59Z
    Key
    ECDSA P-384
    Signature
    SHA256-RSA
    SHA-256
    BA06D3D3E348FCE7478CC84B422D0E638E9E221EF1A0B53ADC14CC70E04B8AB8

Path a browser builds: example.com › Cloudflare TLS Issuing ECC CA 3 (SSL Corporation) › SSL.com TLS Transit ECC CA R2 (SSL Corporation) › SSL.com TLS ECC Root CA 2022 (SSL Corporation)

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.

fullchain.pem
-----BEGIN CERTIFICATE-----
MIID5jCCA42gAwIBAgIQBiTQqzEVWHgLfVITuWMYMTAKBggqhkjOPQQDAjBRMQsw
CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSgwJgYDVQQDDB9D
bG91ZGZsYXJlIFRMUyBJc3N1aW5nIEVDQyBDQSAzMB4XDTI2MDcyOTIyMTAwOFoX
DTI2MTAyNzIyMTcyMVowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAAR2Tgmj3bLPRaVN0Vud8FEAUiMz3Z2Bd5lti39uhuvB
ARyn+R6JJkBCv54dlTizzaUBzLnriaPVW9uysYIJXTVio4ICgDCCAnwwDAYDVR0T
AQH/BAIwADAfBgNVHSMEGDAWgBSDA/3n9vVKTRVB9O0iFtMyCj7KZjBsBggrBgEF
BQcBAQRgMF4wOQYIKwYBBQUHMAKGLWh0dHA6Ly9pLmNmLWkuc3NsLmNvbS9DbG91
ZGZsYXJlLVRMUy1JLUUzLmNlcjAhBggrBgEFBQcwAYYVaHR0cDovL28uY2YtaS5z
c2wuY29tMCUGA1UdEQQeMByCC2V4YW1wbGUuY29tgg0qLmV4YW1wbGUuY29tMCMG
A1UdIAQcMBowCAYGZ4EMAQIBMA4GDCsGAQQBgqkwAQMBATATBgNVHSUEDDAKBggr
BgEFBQcDATBTBgNVHR8ETDBKMEigRqBEhkJodHRwOi8vYy5jZi1pLnNzbC5jb20v
YWU4MDFlZDFjNTViYjU3OWQ3OTIwOGIwZDc3MmFjZmI4Y2MzYTIwOC5jcmwwDgYD
VR0PAQH/BAQDAgeAMA8GCSsGAQQBgtpLLAQCBQAwggEEBgorBgEEAdZ5AgQCBIH1
BIHyAPAAdwCUTkOH+uzB74HzGSQmqBhlAcfTXzgCAT9yZ31VNy4Z2AAAAZ+v9sM2
AAAEAwBIMEYCIQD9WFotRGzWRjLUpKu5UgFVEIW2JB7MtvZe+tocSNgcyQIhAJCF
dDoCWE99JjFKSmzjeRhbiH0M3Aw+h414y9bGxT+PAHUAyKPEf8ezrbk1awE/anoS
beM6TkOlxkb5l605dZkdz5oAAAGfr/bDTAAABAMARjBEAiAKprPtjMQLlLrSks4e
CDoJZ6WqekRLH6AWHSHco9LXtQIgMsRhNtbw0Gp9Q0ItZB5D/0qTzrPKMBDbJZor
+NZkce4wCgYIKoZIzj0EAwIDRwAwRAIgELh9REqDsIBMBAkADWsc3iuhbkwHyfcv
6w+HsjhdPcwCIDzda23fZzKA2+qG5L/k1ti5g4rk3WiJU0UbvpUGLKKv
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC4jCCAmmgAwIBAgIQMe7oivuHzZ74M2YEdD+bJzAKBggqhkjOPQQDAzBPMQsw
CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSYwJAYDVQQDDB1T
U0wuY29tIFRMUyBUcmFuc2l0IEVDQyBDQSBSMjAeFw0yNTA1MjkxOTQ5NDVaFw0z
NTA1MjcxOTQ5NDRaMFExCzAJBgNVBAYTAlVTMRgwFgYDVQQKDA9TU0wgQ29ycG9y
YXRpb24xKDAmBgNVBAMMH0Nsb3VkZmxhcmUgVExTIElzc3VpbmcgRUNDIENBIDMw
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT9HRQUjiNCkybPcNCJ17Eecqw+XVlS
gd8c9dYludZ3B4ULjRKxKz9AuR9yBuD/QFIKk0n4yXGwfu7go0VRGER0o4IBIzCC
AR8wEgYDVR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBQyosfYWIv/f8A88lVp
M+zOzB+8lzBIBggrBgEFBQcBAQQ8MDowOAYIKwYBBQUHMAKGLGh0dHA6Ly9jZXJ0
LnNzbC5jb20vU1NMLmNvbS1UTFMtVC1FQ0MtUjIuY2VyMBEGA1UdIAQKMAgwBgYE
VR0gADAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwPQYDVR0fBDYwNDAy
oDCgLoYsaHR0cDovL2NybHMuc3NsLmNvbS9TU0wuY29tLVRMUy1ULUVDQy1SMi5j
cmwwHQYDVR0OBBYEFIMD/ef29UpNFUH07SIW0zIKPspmMA4GA1UdDwEB/wQEAwIB
hjAKBggqhkjOPQQDAwNnADBkAjBkW/dgTArl36px6LVMWzfHLv0pFD+P9BoOYzir
Z2aS2IwsQPHb0rQdZMY5NghTTk4CMCiwDU71fhtYVRulUc575HJroLXgj+BkpkBu
bdHnb2+aPpCH7LP6QNQ/Xm9NiqdxAw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDNDCCArmgAwIBAgIQYE2K+NALqHSLlVhTFyxfLjAKBggqhkjOPQQDAzBOMQsw
CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT
U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMTAyMTE3MDIyM1oXDTM3
MTAxNzE3MDIyMlowTzELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh
dGlvbjEmMCQGA1UEAwwdU1NMLmNvbSBUTFMgVHJhbnNpdCBFQ0MgQ0EgUjIwdjAQ
BgcqhkjOPQIBBgUrgQQAIgNiAARk532ZA1NckR7q+NgjraG/LOJjie8oaPbt1/Ds
q2iudyvkdpcbUOvbWSgtb7g2uauNl8pMIp7uidkCP/16czqQjSvMLzo3g9oNtC1F
G3NyCWVfeCE954tmP0f9CSnWFA+jggFZMIIBVTASBgNVHRMBAf8ECDAGAQH/AgEB
MB8GA1UdIwQYMBaAFImPL6PoK6AUVHvzVrgmX2c4C5zQMEwGCCsGAQUFBwEBBEAw
PjA8BggrBgEFBQcwAoYwaHR0cDovL2NlcnQuc3NsLmNvbS9TU0xjb20tVExTLVJv
b3QtMjAyMi1FQ0MuY2VyMD8GA1UdIAQ4MDYwNAYEVR0gADAsMCoGCCsGAQUFBwIB
Fh5odHRwczovL3d3dy5zc2wuY29tL3JlcG9zaXRvcnkwHQYDVR0lBBYwFAYIKwYB
BQUHAwIGCCsGAQUFBwMBMEEGA1UdHwQ6MDgwNqA0oDKGMGh0dHA6Ly9jcmxzLnNz
bC5jb20vU1NMY29tLVRMUy1Sb290LTIwMjItRUNDLmNybDAdBgNVHQ4EFgQUMqLH
2FiL/3/APPJVaTPszswfvJcwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2kA
MGYCMQC4SkI+e2cts1nTN9MCRil97z624WxLAp94hT7tNZGPZLe9YiLIyzgKqW/b
E0b2h9ACMQCvV5XMRcunAylQaCQc4J/GwR1p7yrPC0DRWWeyLAkQWi5Ylta9DxlX
74QFFksFCP0=
-----END CERTIFICATE-----

Reproduce what this page saw, from any machine with OpenSSL:

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -c 'BEGIN CERT'
# 1 = leaf only, 2 or more = leaf plus intermediates

curl -vI https://example.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

CheckWhy it matters
Chain completenessWhether 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 verdictsPass, 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 intermediatesThe 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 extrasWhether 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).
ExpiryOf 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 SANWhether 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 keySHA-1 signatures and RSA under 2048 bits, both rejected by current clients.
The fixA 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.

ClientFetchesError on a missing intermediateNotes
Chrome (desktop and Android)yesNET::ERR_CERT_AUTHORITY_INVALIDFetches 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.
FirefoxpreloadsSEC_ERROR_UNKNOWN_ISSUERDoes 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)yesThis 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)yesCERT_E_CHAINING / PartialChainSchannel fetches missing intermediates and downloads roots on demand from Windows Update, so it is the most forgiving client here.
curl and wget (OpenSSL)noSSL certificate problem: unable to get local issuer certificateReads 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_clientnoverify error:num=20:unable to get local issuer certificateThe 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 certificaterequests 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.jsnoUNABLE_TO_VERIFY_LEAF_SIGNATURE: unable to verify the first certificateCompiles 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)nox509: certificate signed by unknown authorityUses 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)noPKIX path building failed: unable to find valid certification path to requested targetUses 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)nocertificate 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)nojavax.net.ssl.SSLHandshakeException: Trust anchor for certification path not foundConscrypt 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 containersnounable to get local issuer certificateWhatever 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

Examples

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.