Error message
x509: certificate has expired or is not yet valid: current time 2026-08-20T15:47:41+03:00 is after 2026-08-19T12:47:41Z
The two timestamps are filled in per failure: the verifier's clock and the certificate's boundary. "is after" means expired; "is before" means not yet valid.
Go crypto/x509 — CertificateInvalidError (Expired)
What it means
The verification time falls outside the certificate's [NotBefore, NotAfter] window. Go states both sides of the comparison in the message, which makes this one of the few errors that carries its own diagnosis: read the two timestamps and see which one is wrong — the certificate's or the clock's.
Why it happens
Renewal automation broke
commonA certbot timer that stopped, an ACME account that lost authorization, or a load balancer serving an old certificate after renewal succeeded elsewhere. With maximum public-cert lifetimes shrinking toward 47 days by 2029, manual renewal is no longer viable.
The verifying machine's clock is wrong
common"is before" failures on devices fresh from boot (Raspberry Pi without RTC, VMs restored from snapshots, IoT hardware) usually mean the clock says 1970 or last month — every current certificate then looks not-yet-valid or expired.
Clock skew at the validity edge
occasionalA certificate issued seconds ago (NotBefore = now) verified by a host a minute behind fails as not-yet-valid; short-lived mesh certificates make this window easy to hit.
How to fix it
- 1.
Read the served certificate's actual dates
Confirm which certificate the endpoint serves right now — after a renewal, "expired" often means "the new cert exists but is not being served". Reload/restart the terminating proxy if dates on disk and on the wire differ.
bash echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \ | openssl x509 -noout -dates # notBefore=... notAfter=... - 2.
Fix and monitor time sync
For "is before" cases, correct the clock and keep it corrected; certificate validation is only as good as local time.
bash timedatectl status # NTP active? sudo timedatectl set-ntp true - 3.
Automate renewal and alert on remaining lifetime
Renew at 2/3 of lifetime (certbot's default), and monitor expiry from outside the host. Our domain Signature Health check grades exactly this — chain, dates and the new shortened-lifetime rules — with free expiry alerts.