openssl x509 -in leaf.pem -noout -enddate # notAfter=Dec 31 23:59:59 2035 GMTopenssl 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 nowif 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: s_client prints it, x509 reads the dateopenssl s_client -connect api.example.com:8443 -servername api.example.com -CAfile root.pem </dev/null 2>/dev/null \ | openssl x509 -noout -enddate# s_client prints the certificate even when verification fails; the result is a separate lineopenssl 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 -enddategrep -m1 'Verify return code' session.txtHow it works
openssl x509 -in leaf.pem -noout -enddateprintsnotAfter=Dec 31 23:59:59 2035 GMT;-dateopt iso_8601(OpenSSL 3.2+) prints2035-12-31 23:59:59Z.-checkend Nexits 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.- For a server,
openssl s_client -connect host:port -servername host </dev/nullprints the certificate it receives;openssl x509 -noout -enddatereads the date from that output.-servernamesends SNI, so the server picks the right certificate, and</dev/nulllets s_client exit instead of waiting for input. - Days left need date arithmetic outside OpenSSL; GNU
date -dparses 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 GMTfrom 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 x509reads only the first certificate of a file: on a bundle of leaf and intermediate it prints the leaf'snotAfter=Dec 31 23:59:59 2035 GMTand ignores the rest.openssl storeutl -noout -text -certs chain.pemlists every certificate, intermediate (Jan 1 00:00:00 2041 GMT) included.-checkend 0on an expired certificate printsCertificate will expireand exits with 1 — the wording does not distinguish "expires soon" from "has expired"; read-enddatewhen you need to tell them apart.- On macOS and BSD,
date -ddoes not exist; parse the date withdate -j -f "%b %e %T %Y %Z"instead.