SSL certificate expired: why it happened and how to renew it
When an SSL certificate has expired, browsers block the site with NET::ERR_CERT_DATE_INVALID or “Your connection is not private”, and API clients refuse to connect. Confirm the expiry date with openssl, renew the certificate (certbot renew, your reverse proxy or your platform), and reload whatever serves it. Certificates from Let’s Encrypt are short-lived and renew themselves, so an expired one almost always means the automatic renewal failed quietly weeks before.
Check when the certificate expires
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates -issuer -subjectnotAfter is the expiry date, and issuer shows who issued it. If the issuer is “TRAEFIK DEFAULT CERT” or the subject names another domain, the server never got a real certificate for this name; that is a missing certificate, not an expired one. On a server that uses Certbot, sudo certbot certificates lists every certificate with its expiry.
Why automatic renewal failed
| Cause | How to tell |
|---|---|
| The renewal timer stopped | systemctl list-timers | grep certbot shows no timer (and there is no /etc/cron.d/certbot), or the server was rebuilt without it |
| Port 80 is closed | The HTTP-01 challenge needs port 80 open to the internet; a firewall rule added later blocks renewal |
| DNS moved | The domain now points at another server, a load balancer or a CDN, so the challenge never reaches you |
| A CAA record excludes the issuer | dig CAA example.com |
| Renewed, but not reloaded | The new certificate is on disk but nginx or the app still serves the old one from memory |
| Rate limits | Too many failed attempts for the same name; the renewal log says so |
Renew the certificate
Certbot on a VPS:
sudo certbot renew --dry-run # test first; shows the error if renewal would fail
sudo certbot renew
sudo systemctl reload nginx # or apache2, or whatever serves the certificateTraefik, the proxy in Coolify and Dokploy, requests and renews certificates itself once the domain resolves to the server and port 80 is open. Its log says why a request failed:
docker logs coolify-proxy 2>&1 | grep -i acme # Coolify
docker logs dokploy-traefik 2>&1 | grep -i acme # DokployPlatforms such as Vercel, Netlify, Cloudflare and DigitalOcean App Platform issue and renew certificates for you while the domain’s DNS points at them. When one lapses there, check that the DNS records still match what the platform’s domain settings ask for, and that no CAA record blocks its issuer.
Stop it happening again
Run certbot renew --dry-run after any firewall, DNS or proxy change, keep port 80 open for the challenge, and add a reload hook (--deploy-hook "systemctl reload nginx") so a renewed certificate is actually served. Renewal failures are silent, so the safeguard that matters is an alert on the expiry date itself, from outside the server, well before the day. A site that goes down for other reasons is covered in 5xx errors explained.