Kubernetes TLS Certificate Decoder
Paste a certificate, a chain, or a whole kubernetes.io/tls Secret, and read what is actually in it: expiry, the names it covers, key size, signature algorithm and chain order. It runs in this tab, which matters because a TLS Secret contains the private key. That key is never decoded or shown.
Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.
The answer appears here
Paste on the left and press Decode. Nothing leaves this tab.
Formatted
Results
Nothing else to flag.
No formatting problems, and nothing the rules object to. Worth remembering what that covers: this reads the file you pasted, not the account or cluster it will be applied to.
No finding matches that filter.
Common mistakes
These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.
Checking the expiry and not the chain
A certificate valid on its own still fails if an intermediate is missing from the bundle, and browsers differ on whether they fetch it.
Instead:Serve the full chain, leaf first, without the root.
Reading the Common Name for the hostname
CN has been ignored by browsers for years. The Subject Alternative Name is what is matched.
Instead:Check the SAN list. A certificate with only a CN fails everywhere modern.
Assuming the whole chain expires together
The leaf, the intermediates and the root have separate dates, and an intermediate expiring breaks everything under it.
Instead:Track all of them, not just the leaf.
What it checks
All of it is read out of the bytes of the certificate, so you can know it before the certificate reaches a server rather than after somebody reports that the site is broken on their phone.
commonName, which nothing reads any more
Browsers stopped using commonName for hostname matching in 2017, with Chrome 58, and every current one looks at subjectAltName only. A certificate with a correct CN and no SAN is rejected outright with ERR_CERT_COMMON_NAME_INVALID, which names the field that is present rather than the one that is missing. That is reported first, before anything else.
What a wildcard actually covers
A wildcard replaces exactly one label. *.example.com covers a.example.com, does not cover example.com, and does not cover a.b.example.com. Both halves of that catch people, so every name is listed with what it matches spelled out, and a wildcard with no apex beside it is flagged.
Chain order, and the missing intermediate
A tls.crt is read in order: leaf first, then each issuer above it, root omitted. Out of order or short an intermediate, it still works in the browser you tested with, because that browser cached the intermediate from another site. It fails on curl, on a phone, and inside the cluster. The order is checked by matching each issuer name to the next subject.
The Secret keys an Ingress silently ignores
A kubernetes.io/tls Secret has to use exactly tls.crt and tls.key. Call one of them cert.pem and the Ingress controller does not error and does not log anything at a level you will see: it serves its own default self-signed certificate, so the symptom is a browser warning naming a hostname you have never heard of.
Keys and signatures that clients refuse
RSA below 2048 bits is rejected by every current client, and OpenSSL calls it ee key too small rather than anything about the size. SHA-1 signatures have been refused since 2017 with no click-through. ECDSA P-256 is stronger than RSA 3072 and smaller, and secp256k1 is the curve that looks fine in openssl and fails in every browser.
The private key, which is never decoded
A TLS Secret contains the private key, which is the reason a decoder that posts to a server is the wrong tool for this file. If tls.key is present, or a key block is pasted alongside the certificate, it is recorded as present and nothing else is done with it. It is not decoded, not parsed and not printed.
What a browser actually checks, and in what order
A certificate is a signed statement that a public key belongs to a set of names, valid between two dates. Almost every TLS failure is one of four things: the name does not match, the dates do not cover now, the chain does not reach a root the client trusts, or the algorithm is one the client stopped accepting. Everything a decoder can tell you is about the first two and the shape of the third.
The name check reads subjectAltName and nothing else
commonName is a field in the subject distinguished name, and it is what every tool prints at the top when it shows you a certificate. It has not been used for hostname matching by Chrome since version 58 in 2017, by Firefox since 48, or by Safari, and RFC 2818 deprecated it in 2000. If subjectAltName is absent the certificate matches no hostname at all, however right the CN looks.
subject: CN=api.example.com <- printed everywhere, read by nothing
X509v3 Subject Alternative Name:
DNS:api.example.com <- this is the only thing matched
no SAN -> ERR_CERT_COMMON_NAME_INVALID
the error names the CN, which is the field that is fine A wildcard is one label, and only the leftmost one
The star stands in for exactly one whole label. It cannot cover the bare domain, it cannot cover two levels, and it cannot be part of a label. That last rule is where RFC 6125 leaves room and browsers do not: a name like api-*.example.com is skipped during matching rather than reported as malformed, so the certificate looks valid and one hostname quietly does not work.
SAN: DNS:*.example.com
a.example.com matches
www.example.com matches
example.com NO. the apex is a different name
a.b.example.com NO. the star is one label, not many
to cover both, list both:
DNS:*.example.com, DNS:example.com Order in tls.crt is not cosmetic
A server sends the file as it is written. A client works from the first certificate, then looks for its issuer, and stops when it reaches something it already trusts. Leaf first, then intermediates, root left out because the client either has it or cannot trust it anyway. Get the order wrong or leave out the intermediate and desktop browsers usually paper over it, from a cached intermediate or by fetching one from the AIA URL. Nothing else does.
tls.crt
-----BEGIN CERTIFICATE----- leaf CN=api.example.com
-----BEGIN CERTIFICATE----- intermediate CN=R11
root omitted
works in your browser, fails in curl
= a missing or out of order intermediate, every time
check it from outside:
openssl s_client -connect api.example.com:443 -showcerts A Kubernetes TLS Secret is two fixed key names
The type kubernetes.io/tls requires the keys tls.crt and tls.key, and the API server checks that both exist. What it does not check is anything else about them. An Ingress controller reads those two names and no others, and when it cannot find them it falls back to its own default certificate rather than failing, which is why a typo in a key name looks like a routing problem.
apiVersion: v1
kind: Secret
metadata:
name: api-tls
type: kubernetes.io/tls <- not Opaque
data:
tls.crt: <base64 of leaf + intermediates, in that order>
tls.key: <base64 of the private key>
kubectl create secret tls api-tls --cert=fullchain.pem --key=key.pem
base64 is not encryption. This object is the private key. Dates, and the two ways they go wrong
Expiry is the obvious one and it is silent: nothing warns you, there is no event and no log line. The less obvious one is notBefore in the future, which produces the same rejection as an expired certificate and is nearly always clock skew between the machine that issued it and the machine serving it. A public certificate longer than 398 days is also refused by Safari and Chrome, whatever the CA says, though a certificate from a private CA you installed yourself is exempt.
notBefore 2026-07-27T12:33:43Z
notAfter 2026-10-25T12:33:43Z 89 days left
notBefore in the future -> same error as expired
check the clock on both ends
public certificate over 398 days -> rejected by the client
private CA over 398 days -> fine, it is your trust store What no decoder can tell you
Whether the signature is genuine, whether the chain reaches a root you trust, and whether the certificate has been revoked. The first needs the issuer's public key, the second needs a trust store, and the third needs OCSP or a CRL over the network. This page makes no network requests, so it does none of the three, and every result says so rather than letting a clean decode imply more than was checked.
verify a path: openssl verify -CAfile roots.pem chain.pem
see what is served: openssl s_client -connect host:443 -showcerts
read a CSR: openssl req -in file.csr -noout -text