A TLS certificate (still often called “SSL certificate” out of habit: SSL has been deprecated since 2015) is a small file that proves “I am example.com, and here’s a public key the world can use to encrypt traffic to me”.
The proof comes from a digital signature by a Certificate Authority (CA) that the client already trusts.
Crucially, a certificate is always half of a keypair, not a thing in itself:
The public key lives inside the certificate. The CA signs it. It’s distributed freely: anyone connecting to your server downloads it during the TLS handshake.
The private key is a separate file kept on the server, never shared. It’s what proves the server actually owns the identity the certificate claims.
Linux defaults. Concatenable: multiple PEM blocks in one file = chain
DER
Raw binary
.der, .cer
Java keystores, some Windows tools
PKCS#7
Container, no private key
.p7b, .p7c
Microsoft IIS, S/MIME
PKCS#12
Container, includes private key, password-protected
.pfx, .p12
Windows certificate store, Java keystores, mobile profile install
INFO
.crt and .cer are ambiguous: they can be either PEM or DER.
Open the file: if it starts with -----BEGIN, it’s PEM, if it’s binary garbage, it’s DER.
Commands
Inspecting a certificate
The Swiss army knife is openssl x509.
Most-used invocations:
# Full human-readable dumpopenssl x509 -in cert.pem -text -noout# Just subject + issuer + validity (the 80% case)openssl x509 -in cert.pem -noout -subject -issuer -dates# SAN list (the real domains it covers, ignoring CN)openssl x509 -in cert.pem -noout -ext subjectAltName# Fingerprints (for pinning / matching)openssl x509 -in cert.pem -noout -fingerprint -sha256openssl x509 -in cert.pem -noout -fingerprint -sha1
If the file is DER instead of PEM, add -inform DER:
openssl x509 -in cert.der -inform DER -text -noout
Check expiration
This is the operational question that comes up the most.
One-shot check on a file
# Get just the expiry dateopenssl x509 -in cert.pem -noout -enddate# notAfter=Aug 15 10:30:00 2026 GMT
One-shot check on a live server
Useful when you want to verify what the server is actually serving (which might differ from what’s in /etc/letsencrypt/... if nginx was never reloaded):
When extracting from PKCS#12, -nodes (“no DES”) means “don’t encrypt the output key with a passphrase”. Skip it if you want the output key still encrypted.
Connect to a live server: openssl s_client
The classic “what’s actually happening on the wire” tool:
# Basic handshake + cert dumpopenssl s_client -connect example.com:443 -servername example.com# Test a specific TLS versionopenssl s_client -connect example.com:443 -servername example.com -tls1_3# Test a specific cipher suiteopenssl s_client -connect example.com:443 -servername example.com -cipher 'ECDHE-RSA-AES256-GCM-SHA384'# StartTLS (SMTP, IMAP, POP3, FTP, LDAP, ...)openssl s_client -connect smtp.example.com:587 -starttls smtpopenssl s_client -connect imap.example.com:143 -starttls imap
s_client stays connected: press Ctrl+D or send Q to close.
For HTTP-style debugging (verify TLS + actually do a GET):
The file must be PEM-formatted and have a .crt extension.
After this, curl, git, wget and most CLI tools using OpenSSL trust the CA automatically.
WARNING
Browsers (Firefox, Chrome) maintain their own trust stores and ignore the system one on most distros. To trust a custom CA in the browser, import it via the browser’s own settings.
Common pitfalls
WARNING
Incomplete chain. Browsers usually handle this gracefully because they cache intermediates.
CLI tools (curl, wget, monitoring scripts) often don’t and fail with “certificate verify failed”.
Always serve fullchain.pem, not just cert.pem.
WARNING
Cert/key mismatch after a manual swap. Symptom: nginx refuses to reload with “SSL_CTX_use_PrivateKey_file failed”. Quick check: the modulus trick from above.
WARNING
Wildcards vs SAN. A *.example.com cert covers foo.example.com but notbar.foo.example.com (one level deep only) and notexample.com itself (you need to add the apex to the SAN list separately).
WARNING
Self-signed certs in production. Acceptable only for internal labs behind their own trust boundary.
For anything user-facing, get a free Let’s Encrypt cert via Certbot: there’s no good reason to ship self-signed in 2025.
Where to go next
Certbot setup guide: for issuing and auto-renewing Let’s Encrypt certificates (the part this guide intentionally skips).
Nginx setup: where the cert is actually plugged in to serve HTTPS.