Published
A TLS certificate is a signed statement: this public key belongs to these names, says this issuer, until this date. Almost every certificate problem you will debug is a failure of one of those four clauses, and reading the certificate tells you which one in about thirty seconds — once you know where to look.
What a certificate actually asserts
Strip away the encoding and an X.509 certificate contains a public key, a set of names, a validity window, some usage constraints, and an issuer's signature over all of it. The private key is not in there and never travels with it, which is why a certificate is public information — and in fact published, in the Certificate Transparency logs.
The trust decision a browser makes is a chain of exactly this assertion repeated. Your server's certificate is signed by an intermediate CA. The intermediate's certificate is signed by a root CA. The root's certificate is in the operating system or browser trust store, put there by a program that audits CAs. If every link verifies, the names are matched, and no link has expired or been revoked, the connection is trusted.
Reading the fields
Below is real output from openssl x509 -text over a certificate generated locally for this guide, trimmed to the parts that matter. Run the same command against any certificate you have on disk, or fetch a live one with openssl s_client -connect example.com:443 -servername example.com and pipe it in.
The subject's Common Name is the field everyone looks at first and it is the one that matters least. Hostname matching has been performed against the Subject Alternative Name extension exclusively for years — Chrome stopped falling back to CN in 2017, and the other major browsers followed. A certificate whose CN names your host but whose SAN list does not will be rejected. If you are debugging a name mismatch, read the SAN and ignore the CN.
Certificate lifetimes are also much shorter than long-serving engineers expect. The CA/Browser Forum baseline requirements capped public certificates at 398 days in 2020, and a further reduction is now in effect: certificates issued from 15 March 2026 are limited to 200 days, dropping to 100 days in March 2027 and 47 days in March 2029. Any renewal process that involves a human remembering something is already obsolete; automate it with ACME.
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
28:c5:03:7d:36:fe:f0:59:70:25:8c:88:ad:46:36:dd:2c:9f:04:5d
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, O=Example Demo CA, CN=Example Demo Root CA
Validity
Not Before: Sep 21 07:06:49 2026 GMT
Not After : Dec 20 07:06:49 2026 GMT
Subject: C=US, O=Example Ltd, CN=www.example.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:www.example.com, DNS:example.com
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Subject Key Identifier:
0E:7D:D3:4E:51:37:FB:5A:28:E3:BE:BC:1B:53:18:8A:CC:E4:B5:D2
X509v3 Authority Key Identifier:
FC:D1:70:93:E7:33:87:D7:E2:87:45:2D:53:7D:77:A4:FD:8F:E8:96
# This certificate and its issuing CA were generated locally while
# writing this guide. The values are real but the CA is not trusted
# by anything, which is why the chain section below matters.SAN, wildcards and name matching
The Subject Alternative Name extension is a list, and a certificate is valid for a hostname only if that hostname appears in it. Entries are usually DNS names; they can also be IP addresses, which is how you get a certificate for a bare IP, and email addresses or URIs for non-web uses.
Wildcard entries follow one rule that trips people constantly: a wildcard replaces exactly one label, and only the leftmost one. A certificate for *.example.com covers www.example.com and api.example.com. It does not cover example.com itself — the apex needs its own SAN entry, which is why almost every real certificate lists both. And it does not cover a.b.example.com, because that is two labels deep. If a subdomain of a subdomain is failing while its siblings work, this is why.
Chain building, and why it is usually the problem
When a client connects, the server sends its own certificate and should also send every intermediate needed to reach a trusted root. The root itself should not be sent: the client already has it.
Omitting the intermediate is the single most common TLS misconfiguration, and it has a characteristic signature: the site works in your browser and fails in curl, in a mobile app, or in a server-to-server call. The reason is that browsers quietly paper over the mistake. Most desktop browsers cache intermediates they have seen elsewhere, and will also follow the Authority Information Access extension to fetch a missing issuer over HTTP. Command-line clients and many language runtimes do neither, so they see a chain that does not reach a root and refuse. If a colleague says the site is fine and your deploy script disagrees, check the chain before checking anything else.
The fix is to serve the full chain: concatenate your leaf certificate and the intermediates into one PEM file, leaf first, and point your server at that. Most ACME clients already produce a fullchain.pem for exactly this reason, and the mistake is usually pointing at cert.pem instead.
$ openssl s_client -connect example.com:443 -servername example.com \
-showcerts < /dev/null
# Read the "Certificate chain" block at the top. You want:
# 0 s:CN=www.example.com i:CN=Some Intermediate CA
# 1 s:CN=Some Intermediate CA i:CN=Some Root CA
#
# Only depth 0 present means the intermediate is missing. Browsers
# may hide this; curl and most SDKs will not.
$ openssl verify -untrusted chain.pem leaf.crt
leaf.crt: OKFormats: what is in the file you were sent
Certificate file extensions are close to meaningless — .cer can be either PEM or DER, and .crt is used for both leaves and CA bundles — so identify the file by its contents rather than its name. If it starts with five dashes and the word BEGIN, it is PEM: a Base64 wrapper around the DER bytes, with a label that tells you what is inside. If the first byte is 0x30, it is raw DER.
The one distinction that genuinely matters is whether a file contains a private key. PEM certificates and PKCS#7 bundles do not. PKCS#8 key files and PKCS#12 archives do. Treat those two the way you would treat any other secret: they should not be attached to a ticket, committed to a repository, or pasted into a web page.
| Extension | Encoding | Contains | Private key inside? |
|---|---|---|---|
| .pem | Base64 text with BEGIN/END labels | One or more certificates, and/or a key — the label says which | Only if a KEY label is present |
| .crt / .cer | PEM or DER — check the first bytes | A certificate, or a concatenated bundle | No |
| .der | Binary DER | Exactly one certificate | No |
| .key | Usually PEM-wrapped PKCS#8 or PKCS#1 | A private key, optionally passphrase-encrypted | Yes |
| .csr | PEM, label CERTIFICATE REQUEST | A public key plus requested names, self-signed as proof of possession | No |
| .p12 / .pfx | Binary PKCS#12, password-protected | A leaf, its chain, and the private key together | Yes |
| .p7b / .p7c | PKCS#7, PEM or DER | Certificates only — typically a chain for import | No |
| .jks | Java KeyStore, password-protected | Certificates and keys in a Java-specific container | Usually |
Fingerprints, and what they identify
A certificate fingerprint is simply a hash — normally SHA-256 — computed over the certificate's complete DER encoding. It is not a field inside the certificate; it is derived from it, which is why two tools always agree on it and why any change to any field produces a completely different fingerprint.
That derivation has a practical consequence people get wrong. Renewing a certificate produces a new fingerprint even if the key, the names and the CA are unchanged, because the serial number and validity dates changed. So a system that pins a certificate fingerprint will break on every renewal, which with 200-day and soon 100-day lifetimes means several times a year.
If you need pinning, pin the Subject Public Key Info hash instead. The SPKI survives renewal as long as you reuse the key pair, which is the behaviour you actually wanted. And pin with a backup: at least one spare key's SPKI, kept offline, so that an emergency key rotation does not lock out every client. Fingerprints remain the right tool for the job they are good at — confirming out-of-band that the certificate you are looking at is the one someone else is looking at.
Reading the errors, and using a decoder sensibly
Browser certificate errors are more specific than their scary presentation suggests, and each maps to a field you can go and read. ERR_CERT_DATE_INVALID means the clock is outside the validity window — check Not After first, then check that the client's own clock is right, because a device with a wrong date produces this on every site. ERR_CERT_COMMON_NAME_INVALID means the hostname is not in the SAN list. ERR_CERT_AUTHORITY_INVALID, or Firefox's SEC_ERROR_UNKNOWN_ISSUER, means the chain did not reach a trusted root — in production that is almost always a missing intermediate, and on a corporate network it is usually a TLS-inspecting proxy whose root is not installed in that particular trust store. ERR_CERT_REVOKED means the CA has published that the certificate should no longer be used.
Our certificate decoder parses a PEM or DER certificate in the page, laying out the subject, SAN list, issuer, validity, key usage and fingerprints. Nothing is uploaded.
Two honest limits. A decoder reads one certificate; it does not build a chain, because it does not have your trust store, cannot fetch the issuer, and cannot know what your server actually sends. When the symptom is a trust failure rather than a wrong field, openssl s_client or an online chain checker is the tool you want. And there is a file you should never paste into it, or into any web page: a .key file or a .p12 archive. Those carry the private key. Decoding a certificate is reading public information; decoding a key file would be handing over the thing the certificate exists to protect. If you have been sent a bundle and you are not sure which it is, open it in a text editor and look for the word PRIVATE before it goes anywhere near a browser.
What to remember
- Hostname matching uses the Subject Alternative Name list only — read the SAN and ignore the Common Name when debugging a name mismatch.
- A wildcard covers exactly one label and never the apex, so *.example.com does not match example.com or a.b.example.com.
- Works in the browser but fails in curl almost always means a missing intermediate; serve the full chain, leaf first.
- Certificate fingerprints change on every renewal, so pin the SPKI hash with an offline backup key rather than the certificate fingerprint.
- Decode certificates freely — they are public — but never paste a .key file or .p12 archive into any web tool, because those contain the private key.