dotvitals

TLS certificate has expired

CriticalConfirmedQuick wintls.certificate.expired

What this check looks for

The certificate's end date has passed. Every browser now shows a full-page security warning instead of your site, and most visitors will not click through it.

Why it matters

This is not a warning about the future — the site is already broken for every visitor, every API client and every payment integration that talks to it. Apps and scripts fail outright, with no interstitial to click past.

When the check passes, your report says: “The certificate is within its validity dates”.

What it costs your score

When this check fails it removes 60 points from your TLS score, before the status, confidence and repeat multipliers are applied. TLS carries a weight of 15 in the overall score.

It shares the tls.certificate family ceiling of 60 points: however many findings that family produces, together they cannot remove more than that from TLS. One underlying problem showing up in several places is still one problem.

Severity
critical
Default confidence
confirmed
Status when triggered
fail
Deduction
60 points
Family cap
tls.certificate · 60
Category
TLS
Module
Tls certificate
Fix owned by
user
In the ruleset since
2026.09

How the whole score is calculated

How to fix it

Issue and install a new certificate now, then fix whatever stopped the renewal.

The site is down for anyone whose client validates certificates, which is all of them.

  1. Issue a replacement immediately — with ACME this is usually a single command.

  2. Install it and reload the server; a reload, not a config test, is what picks it up.

  3. Then find the cause: an expired ACME account, a failing hook, a firewall change blocking the HTTP-01 challenge, or a renewal cron that was never running on this host.

  4. Set up expiry monitoring that alerts at 21 days, so the next failure is noticed before it lands.

How to confirm it worked

  • openssl s_client -connect ‹host›:443 -servername ‹host› </dev/null 2>/dev/null | openssl x509 -noout -dates — expect a notAfter in the future

A named slot like ‹domain› — and the braces left in the configuration below — is filled in with your own values when this rule appears on a report.

Remediation by platform

nginx
certbot renew --force-renewal --cert-name ‹host›
nginx -t && systemctl reload nginx
Apache
certbot renew --force-renewal --cert-name ‹host›
apachectl configtest && systemctl reload apache2
Caddy
# Caddy renews on its own. An expired certificate means renewal has been failing:
journalctl -u caddy --since '30 days ago' | grep -i 'obtain\|renew'
caddy reload --config /etc/caddy/Caddyfile
Cloudflare
SSL/TLS → Edge Certificates: check the Universal SSL certificate is Active. SSL/TLS → Overview: set the mode to Full (strict).
  • A reload is required: nginx holds the old certificate in memory until it re-reads it.

  • The usual causes are port 80 being closed to the ACME HTTP-01 challenge or the storage directory not being writable.

  • Cloudflare's certificate covers visitor-to-edge only. If the origin is reachable directly — by IP address, or through a DNS record that is not proxied — its own certificate is unchanged and is what this scan measured. Fix the origin as well, and set SSL/TLS mode to Full (strict) so Cloudflare itself validates it.

Technical detail

The leaf certificate for ‹host› has notAfter=‹not after›, which is ‹days remaining› days in the past. RFC 5280 §6.1.3 makes the validity window a mandatory part of path validation, so no client that validates certificates at all will accept it. Certificate Transparency and OCSP make no difference here; the date alone is fatal.

Standards and references

Test this on your domain

Run the check that produces this finding, on its own, against any domain.

Open the tls certificate checkerBuild the fix

Other tls certificate checks