The idea: a chain of signatures up to a root you already trust

A certificate says "this public key belongs to example.com", and it is signed by an issuer. The browser cannot know every site's key, but it ships with a short list of root certificates, its trust store (about 150 roots). A site's certificate is trusted if it leads, signature by signature, to one of those roots: the leaf (the site) is signed by an intermediate, and the intermediate by the root.

Three certificates left to right. The leaf for example.com, issued by R11, is signed with R11's private key. The intermediate R11, issued by ISRG Root X1, is signed with the root's key. The root ISRG Root X1 signs itself and sits in the browser's trust store. The server sends the leaf and R11; the root comes from the store
The server sends the leaf and the intermediate; the root comes from the browser's own store, and each signature is checked with the key of the certificate above it.

The canvas has the server on the left, the browser in the middle and the certificate authority (Let's Encrypt, with its real intermediate R11 and root ISRG Root X1) on the right. Below is the chain the browser builds, with one row per check. Pick what the server does wrong in the first select, how revocation is checked in the second, whether an attacker blocks the CA in the third, and press Connect. Today is 2026-10-07.

Building the chain

The server sends its certificates in the TLS Certificate message: the leaf first, then the intermediates. The root is not sent; it would not help, since the browser trusts only its own copy. Starting from the leaf, the browser reads the issuer name and looks for a certificate with that subject, first among the ones the server sent, then in its trust store. A root found in the store ends the chain: it is the trust anchor.

The most common setup mistake is a server that sends only its leaf (cert.pem instead of fullchain.pem). The issuer R11 is then nowhere, and the chain cannot be built (Demo 2: server forgot the intermediate). Some browsers hide this by caching intermediates or downloading them from the URL in the certificate (AIA fetching), so the site works in one browser and fails in curl. A certificate that names itself as its issuer is self-signed: fine as a root in the store, rejected anywhere else (Demo 5).

What is checked on every certificate

Once the chain is built, the browser checks it from the root down. One failed check and there is no padlock:

checkon which certificateswhat it means
signatureevery one below the rootverifies with the issuer's public key: the issuer really signed it, and nothing was changed
validity datesevery onetoday is between notBefore and notAfter
basicConstraints CA:TRUEevery issueronly a CA may sign certificates; pathlen:0 means no CA below it
subjectAltName (SAN)leafthe host name in the URL is in the list (*.example.com covers one label)
extended key usageleafserverAuth: made for a TLS server
Certificate Transparencyleafcarries SCTs: proof it was published in public logs
revocationleaf (and intermediates, by pushed lists)the CA has not cancelled it

The basicConstraints check matters more than it looks. Anyone can buy a valid certificate for their own domain. If browsers did not check CA:FALSE, its owner could sign a certificate for bank.com with it, and the chain would verify signature by signature (Demo 6: a leaf used as a CA). Old Internet Explorer and iOS versions had exactly this bug.

Errors and what they mean

what is wrongOpenSSL saysChrome showsTLS alert
intermediate not sentunable to get local issuer certificateNET::ERR_CERT_AUTHORITY_INVALIDunknown_ca
self-signed / unknown rootself-signed certificateNET::ERR_CERT_AUTHORITY_INVALIDunknown_ca
expired (or not yet valid)certificate has expiredNET::ERR_CERT_DATE_INVALIDcertificate_expired
wrong host namehostname mismatchNET::ERR_CERT_COMMON_NAME_INVALIDcertificate_unknown
issuer is not a CAinvalid CA certificateNET::ERR_CERT_INVALIDbad_certificate
revokedcertificate revokedNET::ERR_CERT_REVOKEDcertificate_revoked

An expired certificate (Demo 3) is usually a renewal job that stopped. A wrong host name (Demo 4) is a certificate for example.com served on api.example.com. Note that the clock matters: a client with a wrong date rejects good certificates.

Revocation: CRL, OCSP and stapling

A certificate stays valid until notAfter, even if its private key is stolen. The owner then asks the CA to revoke it, and the CA publishes that. There are three ways for a browser to find out:

Three sequences side by side. CRL: the browser downloads the whole revocation list from the CA, 64 kilobytes, and searches it for the serial. OCSP: the browser sends one serial to the CA's responder and gets good or revoked back, an extra round trip that also tells the CA which site is visited. OCSP stapling: the server asks the responder every few hours and sends the signed answer inside the TLS handshake; the browser talks only to the server
CRL downloads the whole list, OCSP asks about one serial on every visit, and stapling lets the server bring the CA's signed answer along.
  • CRL (certificate revocation list): the CA's signed list of revoked serial numbers, at the URL in the certificate. Complete, but large and refreshed only every few hours or days (Demo 7: the CRL has it).
  • OCSP: ask the CA's responder about one serial; it answers good, revoked or unknown, signed. Small, but an extra round trip on the critical path, and the CA sees every site each user visits.
  • OCSP stapling: the server fetches its own OCSP answer every few hours and sends it in the handshake (CertificateStatus in TLS 1.2, an extension of the certificate entry in TLS 1.3). The answer is signed by the CA, so the server cannot fake it. No extra round trip, no privacy leak (Demo 1).

Soft-fail and must-staple

What if the CA does not answer? Blocking every site whenever an OCSP server is slow would break the web, so browsers soft-fail: they connect anyway. But the attacker who stole the key is often the one on the network path, and can simply drop the packets to the CA. The revoked certificate is then accepted (Demo 8: OCSP blocked). Stapling alone does not fix this: the attacker leaves the staple out.

Must-staple is an extension in the certificate itself (TLS Feature, status_request). It tells the browser: this certificate is never used without a staple. A missing staple is then a hard failure, and the CA does not need to be reached at all (Demo 9). Firefox enforces it; Chrome does not.

Because live checks are weak, browsers now push revocation data to themselves instead: Chrome's CRLSets and Firefox's CRLite download compressed lists of revoked certificates in the background. Let's Encrypt ended its OCSP service in 2025 and publishes CRLs only, and certificate lifetimes keep shrinking (90 days now, 47 days planned by 2029) so a stolen key is useful for less time.

Certificate Transparency

Revocation only helps once someone notices a bad certificate. Certificate Transparency makes every publicly trusted certificate visible: before issuing, the CA submits it to several public, append-only logs and gets back Signed Certificate Timestamps (SCTs), which go into the certificate. Browsers require SCTs, so a certificate cannot be issued in secret. Site owners watch the logs (for example on crt.sh) for certificates for their domains they did not ask for.

See How an HTTPS Connection Is Established for the whole handshake around this step, and Digital Signatures for how one signature is made and verified.

Client certificates are shown on Mutual TLS.

What the page leaves out

Cross-signed roots and several possible chains (path building that tries alternatives), name constraints, key usage bits, policy OIDs, wildcard matching rules, Extended Validation, the OCSP responder's own certificate, CRL partitioning and delta CRLs, how CRLSets and CRLite are built, client certificates (mutual TLS), and the CA/Browser Forum rules that decide which roots go into a trust store.