Check when a certificate expires
To read a certificate's expiry date, run `openssl x509 -in certificate.crt -noout -enddate`. For a live server, pipe `s_client` into `x509`: `openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -enddate`. In monitoring, use `-checkend` instead of parsing dates — `openssl x509 -in certificate.crt -noout -checkend 2592000` exits 0 if the certificate is still valid 30 days from now and 1 if it is not, which is exactly what a check script needs.
From a file #
openssl x509 -in certificate.crt -noout -enddate Expected output
notAfter=Nov 14 23:59:59 2026 GMT
openssl x509 -in certificate.crt -noout -dates Expected output
notBefore=Aug 16 00:00:00 2026 GMT notAfter=Nov 14 23:59:59 2026 GMT
From a live server #
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -enddate Expected output
notAfter=Nov 14 23:59:59 2026 GMT
The form you want in monitoring #
Parsing and comparing dates in shell is fiddly and locale-sensitive. -checkend does the comparison
for you and answers with an exit code:
openssl x509 -in certificate.crt -noout -checkend 2592000 Expected output
Certificate will not expire
if openssl x509 -in certificate.crt -noout -checkend 2592000; then
echo "OK — more than 30 days left"
else
echo "WARNING — expires within 30 days"
fi | Seconds | Window |
|---|---|
0 |
Expired right now |
604800 |
7 days |
2592000 |
30 days |
5184000 |
60 days |
7776000 |
90 days |
Checking a live server in a script #
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -checkend 2592000 Wrapped into something you can run over a list of hosts:
for host in example.com api.example.com www.example.com; do
if openssl s_client -connect "$host:443" -servername "$host" </dev/null 2>/dev/null \
| openssl x509 -noout -checkend 2592000 >/dev/null; then
echo "OK $host"
else
echo "EXPIRING $host"
fi
done Days remaining as a number #
Useful when feeding a metrics system rather than an alert:
end=$(openssl x509 -in certificate.crt -noout -enddate | cut -d= -f2)
echo $(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 )) Do not forget the chain #
A leaf certificate with plenty of time left is still broken if an intermediate above it has expired, and checking only the leaf will not show it:
openssl crl2pkcs7 -nocrl -certfile fullchain.crt \
| openssl pkcs7 -print_certs \
| openssl x509 -noout -subject -enddate 2>/dev/null Frequently asked questions
- What does -checkend return?
- An exit code, not a date. It exits 0 if the certificate is still valid the given number of seconds from now, and 1 if it will have expired. It also prints a line of text, but the exit code is what a script should branch on.
- How do I check expiry without OpenSSL?
- On a live server you can use
curl -vI https://example.comand read the certificate lines, orecho | openssl s_clientif OpenSSL is present but you dislike the syntax. There is no reliable way to read a certificate file's dates without a tool that parses ASN.1. - Why does my certificate look valid but browsers say it is not?
- Check the notBefore date as well as notAfter, and check the whole chain — an expired intermediate invalidates everything below it while the leaf still looks fine. See verifying a chain.
- What time zone are the dates in?
- Always GMT/UTC. X.509 requires it, and OpenSSL prints the GMT suffix to make that explicit.
References
- openssl-x509(1) — -checkend and -dates
- RFC 5280: X.509 and CRL profile — the specification every field on a certificate comes from