Most TLS errors I’ve dealt with came down to one of four things: the certificate had expired, the host name wasn’t in it, the chain was incomplete, or the chain was in the wrong order. All four are visible in the certificate itself, if you know which fields to read.
I used to run openssl x509 -text and scroll. Now I paste into the certificate decoder, which decodes in the browser and puts the important parts first. Here’s what those parts mean.
Getting the certificate
If you have the file, open it — .pem, .crt, .cer, .der and .p7b all work. To grab what a live server is actually sending (which is often not what you think you deployed):
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
Copy every -----BEGIN CERTIFICATE----- block from the output and paste them together. -servername matters: without it, a server hosting several sites may send the wrong certificate.
The decoder deliberately doesn’t fetch certificates from URLs itself — that would need a server making requests on your behalf, and the point is that the tool only processes what you paste, in the page.
Subject and issuer
The subject is who the certificate is for; the issuer is who signed it. Both are distinguished names like:
CN=shop.example.test,O=Bäckerei Shop GmbH,L=München,C=DE
That’s RFC 2253 order — most specific first — which is what openssl x509 -nameopt RFC2253 prints. When subject and issuer are identical, the certificate is self-issued: a root CA, or a self-signed development certificate.
Subject Alternative Names: the field that matters
Here’s the thing that trips people up: browsers ignore the Common Name. Chrome stopped using it in 2017, and every major client followed. A certificate is valid for a host name only if that name is in the Subject Alternative Name (SAN) extension.
A SAN list can contain DNS names (shop.example.test), wildcards (*.shop.example.test, which covers exactly one label — a.shop.example.test but not a.b.shop.example.test or shop.example.test itself), IP addresses, email addresses and URIs. If you generate certificates with OpenSSL, add them explicitly:
openssl req -new -key server.key -subj "/CN=shop.example.test" -addext "subjectAltName = DNS:shop.example.test, DNS:*.shop.example.test"
The decoder warns when a server certificate has no DNS or IP SANs.
Validity and your clock
Not Before and Not After are in UTC. The decoder shows how many days are left, relative to your device’s clock — so if your laptop’s clock is wrong, so is the badge. Publicly trusted TLS certificates have a maximum lifetime, and it’s shrinking: 398 days for certificates issued from September 2020, 200 days from March 15, 2026, 100 days from March 2027 and 47 days from March 2029 (CA/Browser Forum ballot SC-081). The tool flags server certificates that are valid for longer than a public CA could issue on their start date — which is fine for a private CA, and a red flag for anything that’s supposed to be public. It’s also a good argument for automating renewal now.
Key, signature and usage
- Public key: RSA should be 2048 bits or more; ECDSA P-256 is the common modern choice and much smaller.
- Signature algorithm: anything with SHA-1 or MD5 is rejected by browsers.
- Key usage / extended key usage: a server certificate needs “TLS Web Server Authentication”. A certificate that’s missing it may work in one client and fail in another.
- Basic constraints:
CA:TRUEmeans it can issue other certificates. Your server certificate should sayCA:FALSE.
Fingerprints and pins
The SHA-256 fingerprint is the hash of the certificate’s bytes. It’s what you compare when someone says “the cert with fingerprint 41:AD:64…” or when a tool asks you to confirm a certificate. The decoder’s fingerprints match openssl x509 -fingerprint -sha256.
The SPKI pin is different: it hashes only the public key. When a certificate is renewed with the same key, the fingerprint changes but the pin doesn’t — which is why Android’s network security config and other pinning schemes use it.
Chain order
A server should send its own certificate first, then each intermediate, in order. It usually shouldn’t send the root; clients already have it. What I often find in fullchain.pem files is the root first, or intermediates in random order, because someone concatenated files in whatever order ls showed them.
Modern browsers are forgiving: they reorder what they get, and can often fill in a missing intermediate from a cache or the certificate’s “CA Issuers” URL. Many other clients can’t — command-line tools, language runtimes, older phones and embedded TLS stacks — so a chain that “works in Chrome” can still fail from your backend service or an IoT device. Paste the whole chain into the decoder and it:
- works out which certificate issued which (matching issuer to subject, confirmed by the key identifiers),
- shows them leaf → intermediate → root and says if the order you pasted was wrong,
- verifies each signature with the next certificate’s public key, and
- tells you if the chain stops at a certificate whose issuer isn’t in the paste — a missing intermediate.
“Download ordered chain” saves them in the right order for your server config.
CSRs
A certificate signing request contains the subject, public key and requested SANs — the tool shows those and checks that the CSR is signed by its own key. It’s worth decoding every CSR before sending it to a CA: a missing SAN in the CSR is a missing SAN in the certificate you pay for.
Never paste a private key
The decoder refuses private keys — PRIVATE KEY, RSA PRIVATE KEY, EC PRIVATE KEY, encrypted and OpenSSH keys, and DER-encoded keys. It clears the text before anything is parsed and shows a warning. That’s a rule worth keeping everywhere: no website needs your private key to show you a certificate. If you’ve pasted one somewhere, replace it.
For the encoding layer underneath all this, the Base64 tool decodes the PEM body, and the hash generator computes SHA-256 over any file if you want to check a fingerprint by hand.