Check an SSL certificate with openssl

· 3 min read

openssl ships with every Linux and macOS and shows exactly the certificate a server serves, including chain and TLS version. That helps when the browser shows a warning, a client reports IMAP errors or a cron job should check how long a certificate is still valid. This guide covers the commands you need for that.

The connection: openssl s_client

s_client opens a TLS connection and prints what the server presents. -servername matters: without SNI many servers serve the wrong certificate because several domains share one IP. The trailing </dev/null ends the session, otherwise openssl waits for input.

openssl s_client -connect example.com:443 -servername example.com </dev/null

The output contains the certificate chain, the certificate itself in Base64, the issuer, the negotiated TLS version and at the end "Verify return code". 0 (ok) means the chain validates against the system's root certificates. Other codes name the problem: 10 for expired, 21 for an unverifiable first certificate (usually the intermediate is missing), 18 for self-signed.

Read expiry date, issuer and names

The s_client output can be piped into openssl x509, which prints individual fields. That gives one-liners for scripts and cron jobs.

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -ext subjectAltName
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -enddate -checkend 1209600

-dates shows notBefore and notAfter, -ext subjectAltName every host name the certificate covers. -checkend 1209600 checks whether the certificate expires within the next 14 days (in seconds) and returns exit code 1 if so. That is the simplest form of an expiry check in a shell script.

Chain and intermediate certificates

A certificate that works on the developer's machine and fails on Android devices or in mail clients almost always has an incomplete chain: the server sends only its own certificate, not the CA's intermediate. Desktop browsers fetch the missing link themselves, other clients do not. -showcerts prints every certificate the server actually sends.

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -E "^ [0-9]+ s:|^   i:"

Expect a chain of two or three entries: the server certificate (s: with the host name), then the intermediate whose s: matches the i: of the server certificate. If only one entry appears and the verify code is 21, the intermediate is missing from the server configuration: with nginx the fullchain.pem belongs in ssl_certificate, with Apache since 2.4.8 in SSLCertificateFile as well.

Mail servers: SMTP, IMAP and POP3

Mail server certificates expire unnoticed because no browser warns. Ports with implicit TLS (465, 993, 995) work like 443. Ports with STARTTLS (25, 587, 143, 110) start unencrypted and only switch after a command, so openssl needs the -starttls option with the protocol.

openssl s_client -connect mail.example.com:465 -servername mail.example.com </dev/null
openssl s_client -connect mail.example.com:993 -servername mail.example.com </dev/null
openssl s_client -connect mail.example.com:587 -starttls smtp -servername mail.example.com </dev/null
openssl s_client -connect mail.example.com:143 -starttls imap -servername mail.example.com </dev/null
openssl s_client -connect mail.example.com:25 -starttls smtp </dev/null

The host name in the certificate must match the one mail clients use. A server configured as mail.example.com that serves a certificate for server42.host.com produces warnings in Outlook and Apple Mail on every start. For the MX the host name from the MX record counts, not the domain.

TLS version and cipher

With -tls1_2 or -tls1_3 you force a version and see whether the server offers it. If -tls1_3 fails, the server does not speak TLS 1.3 yet; if -tls1 or -tls1_1 succeeds, outdated versions are still active and should be disabled.

openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/null 2>&1 | grep -E "Protocol|Cipher"
openssl s_client -connect example.com:443 -servername example.com -tls1_1 </dev/null 2>&1 | grep -E "Protocol|error"

Inspect a certificate file locally

Before installing, a certificate file can be read without a server, and a common mistake during a switch is a certificate that does not match the private key. If the two modulus hashes are identical, certificate and key belong together.

openssl x509 -in cert.pem -noout -text
openssl x509 -in cert.pem -noout -modulus | openssl md5
openssl rsa -in privkey.pem -noout -modulus | openssl md5

From one-off checks to monitoring

The -checkend one-liner in a cron job is better than nothing, but it only checks the hosts someone added and sends its mail to a mailbox someone has to read. For many client domains including mail servers DomainWarn takes over: it checks every certificate every six hours, alerts 30 and 14 days before expiry, detects chain errors and matching host names and shows in the daily digest which certificates are due next.

Frequently asked questions

openssl reports "unable to get local issuer certificate" but the certificate is valid.
Either the intermediate is missing from the server configuration (check with -showcerts), or the system has outdated root certificates. On macOS use -CAfile with a current CA bundle, on Linux update the ca-certificates package.
Why does openssl show a different certificate than the browser?
Usually -servername is missing. Without SNI the server serves its default certificate, which belongs to another domain on the same IP.
How do I check the certificate on a server that is not in DNS yet?
Use -connect with the IP address and -servername with the future host name: openssl s_client -connect 203.0.113.10:443 -servername example.com. That tests a migration before the A record is switched.
Check now

Free SSL certificate check: expiry date, issuer, chain, host name and TLS version, mail servers too. Detects expired, self-signed and mismatched certificates.

DomainWarn checks SPF, DKIM, DMARC, DNS and certificates of all client domains regularly and reports changes before mail lands in spam.

Monitor this domain continuously14-day free trial, no credit card.

More guides