The four conditions every browser checks
A certificate is not simply "valid" or "invalid". A browser runs four independent tests, and a failure in any one of them produces a full-page interstitial. Reading them separately is the difference between a five-minute fix and an afternoon of guessing.
- Validity window. The current time must fall between
notBeforeandnotAfter. Failing this gives ChromeERR_CERT_DATE_INVALID— which also fires when the *client* clock is wrong, so check the visitor's date before you blame the server. - Hostname match. The name in the address bar must match an entry in the Subject Alternative Name extension. Common Name has been ignored by Chrome since version 58 and by Firefox since 48; a certificate with only a CN and no SAN fails everywhere. Wildcards match exactly one label:
*.example.comcoverswww.example.combut notexample.comitself and nota.b.example.com. - Chain to a trusted root. The server must send the leaf plus every intermediate needed to reach a root the client already trusts. The root itself is deliberately not sent — it is already in the trust store.
- Signature and revocation. Every signature in the path must verify, the algorithms must still be acceptable (SHA-1 signatures are rejected outright), and the certificate must not be revoked via OCSP stapling or CRLSets.
How to read the result
Days remaining
The headline number counts down to notAfter in UTC. Automated issuance (ACME, Let's Encrypt, ZeroSSL, most managed hosts) renews at roughly one third of the lifetime remaining — 30 days for a 90-day certificate. If a certificate that should auto-renew is inside 20 days, renewal is failing silently; check the ACME client's logs and its ability to answer the HTTP-01 or DNS-01 challenge. Certificate lifetimes are shrinking industry-wide, so any process that depends on a human remembering a date will eventually fail.
Read the full explanation of ssl certificate checker results ↓Show less ↑
Chain complete / incomplete
This is the single most common production TLS bug, and the most confusing, because it is *invisible from a laptop that has already cached the intermediate*. If the server sends only the leaf, Chrome on your desktop may still succeed — it fetches the missing issuer via the Authority Information Access extension or has it cached from another site. Safari, most Android versions, curl on a minimal container, Java clients, and payment gateways do not. The symptom is "it works for me, but the API integration says unable to get local issuer certificate".
The fix is always the same: concatenate the leaf and the CA-supplied intermediate bundle into the file your server points ssl_certificate (nginx) or SSLCertificateFile (Apache 2.4.8+) at, leaf first. In nginx that single file must contain the full chain — this is exactly what Certbot's fullchain.pem is, and using cert.pem instead is the classic mistake.
Hostname mismatch
The SAN list shown below the verdict is the complete set of names this certificate covers. A mismatch almost always means one of three things: the request hit a default virtual host because SNI was not sent or not configured, the certificate covers the apex but you are testing www (or the reverse), or a CDN is terminating TLS with its own certificate for a hostname you have not added to the CDN configuration.
Self-signed and untrusted
A self-signed certificate has an identical subject and issuer — it vouches for itself. That is fine for an internal service where you distribute the certificate to clients, and fatal for anything a browser visits. "Not trusted" without "self-signed" usually means a private CA (common behind corporate proxies) or an intermediate that expired independently of the leaf.
Common failures and their fixes
| Symptom | Cause | Fix |
|---|---|---|
ERR_CERT_DATE_INVALID on one machine only | Client clock is wrong | Fix NTP on the client; the certificate is fine |
ERR_CERT_AUTHORITY_INVALID | Self-signed, private CA, or missing intermediate | Install the CA bundle; check "chain complete" above |
unable to get local issuer certificate from curl only | Incomplete chain masked by browser AIA fetching | Serve fullchain.pem, not cert.pem |
ERR_CERT_COMMON_NAME_INVALID | Hostname not in the SAN list | Reissue with every hostname you serve, or fix SNI/vhost routing |
| Certificate is correct on disk but stale over the wire | Server never reloaded after renewal | nginx -s reload / systemctl reload apache2; add a deploy hook to the ACME client |
| Renewal succeeds but the old certificate is served | A CDN or load balancer terminates TLS ahead of the origin | Renew at the edge — check the certificate hostname and issuer reported above |
The same checks with openssl
Everything on this page comes from an ordinary TLS handshake, and openssl s_client gives you the same data from any shell. The -servername flag is not optional: without it no SNI is sent and a shared host will hand you the wrong certificate entirely.
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
subject=CN=example.com
issuer=C=US, O=SSL Corporation, CN=Cloudflare TLS Issuing ECC CA 3
notBefore=Jul 29 22:10:08 2026 GMT
notAfter=Oct 27 22:17:21 2026 GMTecho | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -ext subjectAltName
X509v3 Subject Alternative Name:
DNS:example.com, DNS:*.example.comecho | openssl s_client -connect incomplete-chain.badssl.com:443 \
-servername incomplete-chain.badssl.com -showcerts 2>/dev/null \
| grep -E "Verify return code|^ *[0-9]+ s:|^ *[0-9]+ i:"
0 s:CN=*.badssl.com
Verify return code: 21 (unable to verify the first certificate)echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -checkend 2592000
# "Certificate will not expire" -> exit 0
# "Certificate will expire" -> exit 1 (alert on this)For a non-443 service — SMTP on 587, IMAP on 993, a database with TLS — add -starttls smtp (or imap, pop3, postgres) so OpenSSL negotiates the protocol upgrade before the handshake. This tool accepts any port in the field above and speaks TLS directly, which is what implicit-TLS ports such as 465 and 993 expect.
Certificate hygiene worth automating
- Publish a CAA record so only your chosen CA can issue for the domain. It costs one DNS record and blocks an entire class of mis-issuance.
- Alert at 21 days remaining, not 7. A failed ACME renewal usually needs a DNS or firewall change, and that takes longer than a week to get approved in most organisations.
- Monitor the certificate the *edge* serves, not the one on the origin disk. If Cloudflare, Fastly, or a load balancer terminates TLS, that is the certificate visitors actually see.
- Check every hostname separately. Apex and
wwwfrequently diverge after a migration, and only one of them is usually monitored. - After any change, confirm the response really is coming from the new certificate by comparing the SHA-256 fingerprint shown above with the one on the server.
Once TLS is healthy, the next things worth checking are the HTTP response headers your server sends and their security header grade — HSTS in particular only makes sense once the certificate chain is reliably correct.