Skip to content
ecdsa.com

Certificate expiry · OpenSSL

How to check SSL certificate expiration date with OpenSSL

To check SSL certificate expiration date with OpenSSL, run openssl x509 -noout -enddate on the file, or pipe openssl s_client from the server into the same command; -checkend turns the check into an exit code for scripts and cron jobs. This recipe runs every variant against a local test server, including one whose certificate has already expired.

Tested with OpenSSL 3.6.4 (Debian forky (buildpack-deps:forky-curl), GNU date).

notAfter of a file, a 30-day check with -checkend, and days left
openssl x509 -in leaf.pem -noout -enddate # notAfter=Dec 31 23:59:59 2035 GMT
openssl x509 -in leaf.pem -noout -enddate -dateopt iso_8601 # OpenSSL 3.2+
# -checkend N exits with 0 if the certificate is still valid N seconds from now
if openssl x509 -in leaf.pem -noout -checkend $((30 * 86400)) >/dev/null; then
echo "leaf.pem: valid for at least 30 more days"
else
echo "leaf.pem: expires within 30 days, renew"
fi
# Days left, with GNU date (on macOS/BSD: date -j -f "%b %e %T %Y %Z" "$end" +%s)
end=$(openssl x509 -in leaf.pem -noout -enddate | cut -d= -f2)
echo "leaf.pem: $(( ($(date -d "$end" +%s) - $(date +%s)) / 86400 )) days left"
the certificate a server presents, read with s_client
# The certificate a server presents: s_client prints it, x509 reads the date
openssl s_client -connect api.example.com:8443 -servername api.example.com -CAfile root.pem </dev/null 2>/dev/null \
| openssl x509 -noout -enddate
an expired server: the date and the verification result
# s_client prints the certificate even when verification fails; the result is a separate line
openssl s_client -connect expired.example.com:9443 -servername expired.example.com -CAfile root.pem </dev/null 2>/dev/null \
| tee session.txt | openssl x509 -noout -enddate
grep -m1 'Verify return code' session.txt

How it works

  1. openssl x509 -in leaf.pem -noout -enddate prints notAfter=Dec 31 23:59:59 2035 GMT; -dateopt iso_8601 (OpenSSL 3.2+) prints 2035-12-31 23:59:59Z.
  2. -checkend N exits with 0 when the certificate is still valid N seconds from now and with 1 otherwise (Certificate will expire), which is what a monitoring script needs.
  3. For a server, openssl s_client -connect host:port -servername host </dev/null prints the certificate it receives; openssl x509 -noout -enddate reads the date from that output. -servername sends SNI, so the server picks the right certificate, and </dev/null lets s_client exit instead of waiting for input.
  4. Days left need date arithmetic outside OpenSSL; GNU date -d parses OpenSSL's date format directly.

Gotchas

  • s_client prints the certificate even when verification fails, so the pipe reads notAfter=Mar 31 23:59:59 2025 GMT from an expired server too. The verdict is a separate line: Verify return code: 10 (certificate has expired) — grep it if you need to know.
  • openssl x509 reads only the first certificate of a file: on a bundle of leaf and intermediate it prints the leaf's notAfter=Dec 31 23:59:59 2035 GMT and ignores the rest. openssl storeutl -noout -text -certs chain.pem lists every certificate, intermediate (Jan 1 00:00:00 2041 GMT) included.
  • -checkend 0 on an expired certificate prints Certificate will expire and exits with 1 — the wording does not distinguish "expires soon" from "has expired"; read -enddate when you need to tell them apart.
  • On macOS and BSD, date -d does not exist; parse the date with date -j -f "%b %e %T %Y %Z" instead.

Related recipes