Short answer

"Certificate has expired" means the certificate's notAfter date has passed. A certificate is only valid for a fixed window (today typically 90 days to one year for public certificates), and after notAfter clients reject it. The fix is to renew the certificate and reinstall it — but remember to restart the service and to check whether an intermediate has also expired.

Confirm it first

Do not guess — read the dates straight from the server:

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

The output shows notBefore and notAfter. If notAfter is in the past, the certificate has expired. For a local file:

openssl x509 -in certificate.crt -noout -enddate

Why do certificates expire at all?

Short lifetimes limit the damage if a private key leaks, and force keys and cryptography to be refreshed. The industry is moving toward ever-shorter lifetimes, which makes manual renewal unsustainable. We have a dedicated article on why certificates expire.

How to fix it

  1. Renew the certificate with your CA or via ACME (Let's Encrypt, ZeroSSL and others).
  2. Install the new certificate including the full certificate chain (fullchain), not just the leaf certificate.
  3. Restart or reload the service — many handshake failures after a renewal are because nginx/Apache/the load balancer still holds the old certificate in memory.
  4. Verify from the outside with the openssl s_client command above.

The trap: an expired intermediate

Your leaf certificate can be perfectly valid while an intermediate in the chain has expired — and then the whole chain fails anyway. This has caused several large outages when a CA's intermediate expired. Check the whole chain, not just the leaf:

openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null \
  | openssl crl2pkcs7 -nocrl -certfile /dev/stdin \
  | openssl pkcs7 -print_certs -noout -text | grep -E "Subject:|Not After"

If you see a Not After in the past on any certificate in the chain, that is the culprit.

The real fix: stop letting them expire

An expired certificate is never a surprise — the date is written into the certificate from day one. The real problem is lack of visibility: nobody knew that particular certificate existed, or who owned it. CertControl discovers all of your certificates (including the ones nobody remembered), tracks expiry dates and warns you in good time via email, Slack or webhook — so the renewal happens before the failure hits.

Frequently asked questions

How quickly does the site work again after renewal?

As soon as the new certificate is installed and the service is reloaded. There is no propagation delay like DNS — but remember to reload nginx/Apache, otherwise the old certificate is still served from memory.

Can I just set my clock back to avoid the error?

No. That only "fixes" it on your own machine and creates new problems. Clients verify against their own clock, so everyone else still sees an expired certificate. Renew instead.

Why does it still fail right after I renewed?

Three common causes: the service was not reloaded, you installed only the leaf certificate without the chain, or an intermediate in the chain has itself expired.

What is the difference between "expired" and "not yet valid"?

"Expired" means notAfter has passed. "Not yet valid" means notBefore is in the future — often because the server clock is wrong.