certificate has expired

The error "certificate has expired" (OpenSSL verify error 10) means a certificate in the chain is past its `notAfter` date. Check which one before renewing: an expired intermediate invalidates everything below it while the leaf still looks fine, so renewing the leaf would change nothing. Also confirm the machine's clock is correct — a client whose date is wrong reports every valid certificate as expired, and that is a far quicker fix than a reissue.

Updated August 19, 2026 Verified against OpenSSL 3.5.7

Step 1 — check the clock first #

It takes five seconds and it is a surprisingly common cause, especially on containers, virtual machines and devices that have lost power:

date -u

If that is wrong, fix it and retry before touching any certificate. A client whose clock is years ahead reports every certificate on the internet as expired.

Step 2 — find out which certificate expired #

The leaf is the obvious suspect and often not the culprit. Check every certificate the server sends:

Dates for every certificate in the chain
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
| awk '/BEGIN CERT/,/END CERT/' \
| openssl storeutl -noout -text /dev/stdin 2>/dev/null \
| grep -E "Subject:|Not After"

Or from files on disk:

for f in certificate.crt chain.crt; do
echo "== $f"
openssl x509 -in "$f" -noout -subject -enddate
done

Step 3 — renew #

certbot renew --force-renewal
systemctl reload nginx

If renewal is failing rather than merely overdue, run certbot renew --dry-run and read the validation error — it is usually a DNS or webroot problem rather than a certificate one. The ACME challenge types explain which of the two you are looking at.

Generate a fresh key and CSR, submit it, then rebuild the chain:

openssl req -new -newkey rsa:2048 -nodes \
-keyout example.com.key -out example.com.csr \
-subj "/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com"
cat certificate.crt intermediate.crt > fullchain.crt

Step 4 — confirm the new certificate is actually being served #

Reloading is easy to forget, and a service keeps the old certificate in memory until it happens:

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -dates

If the dates are unchanged, the service did not pick up the new file — or something in front of it (a CDN, a load balancer, a reverse proxy) is serving its own copy.

Preventing a repeat #

Alert 30 days out, by exit code
openssl x509 -in certificate.crt -noout -checkend 2592000 \
|| echo "WARNING: expires within 30 days"

Check the whole chain, not just the leaf:

for f in certificate.crt chain.crt; do
openssl x509 -in "$f" -noout -checkend 2592000 >/dev/null \
  || echo "EXPIRING SOON: $f"
done

See checking certificate expiry for monitoring several hosts at once.

Frequently asked questions

Why does the site still fail after I renewed?
Three usual reasons. The service was not reloaded, so it is still serving the old certificate from memory. An intermediate expired rather than the leaf. Or a CDN or load balancer in front holds its own copy that also needs updating.
Can I use an expired certificate temporarily?
Only by disabling verification, which removes the protection TLS provides. There is no way to make clients accept an expired certificate. Renew it — with ACME that is one command.
My certificate is valid but clients say expired — why?
Their clock is wrong. This is common on devices that lost power, on freshly-built virtual machines, and in containers without a time source. Fix the clock rather than the certificate.
How do I stop this happening again?
Monitor with -checkend and alert well before expiry, and automate renewal. Since certificate lifetimes are shrinking, anything renewed by hand will eventually be missed.

References