Skip to main content
All fixes

SSL, TLS, security headers

Fix expired SSL certificate and restore HTTPS access

Your TLS certificate is past its notAfter date and browsers are blocking the page with NET::ERR_CERT_DATE_INVALID. Renew with certbot, ACM, or your CA and reload.

What's happening

An expired SSL certificate means the X.509 leaf served during the TLS handshake has a notAfter date earlier than the current system clock. Once that boundary is crossed, every browser treats the certificate as untrusted and aborts the connection before the application layer is reached.

In Chrome users see a full-page interstitial titled 'Your connection is not private' with the error code NET::ERR_CERT_DATE_INVALID. Firefox displays SEC_ERROR_EXPIRED_CERTIFICATE, and Safari shows 'NSURLErrorServerCertificateUntrusted'. Curl exits with code 60. The Server Hello still completes, but the client rejects the certificate at the verify step.

Expired certificates almost always indicate a broken renewal pipeline rather than a one-off oversight. Either certbot's timer was disabled, the ACME challenge stopped resolving, or the certificate was issued manually and never tracked. The fix is fast, but the long-term fix is automation with monitoring.

Why it matters

Browsers block 100% of organic traffic the moment a certificate expires. There is no soft-fail mode — the user sees an interstitial and bounces. Conversion drops to zero on the affected hostname until the certificate is replaced and the edge is reloaded.

Search engines downgrade or deindex pages served over a broken HTTPS handshake. Googlebot honors the same trust store as Chrome and will mark the URL as 'Soft 404' or 'Crawl error' in Search Console within hours, with ranking decay over the following days.

PCI-DSS 4.0 requirement 4.2.1 requires strong cryptography on all transmitted cardholder data; an expired certificate is a control failure. Expect compliance findings if the outage spans an audit window, plus customer-trust damage that lingers long after renewal.

Common causes

  • Certbot's systemd timer or cron entry was disabled, masked, or never installed in the first place.
  • The ACME HTTP-01 challenge fails because /.well-known/acme-challenge/ returns 404, 301, or 403 from a misconfigured reverse proxy.
  • DNS-01 challenge fails because the API token rotated or the provider plugin was upgraded with breaking changes.
  • The certificate was issued manually from a CA portal and nobody owns the renewal calendar entry.
  • Cloud load balancer (ALB, GCLB) is pinned to a specific ACM certificate ARN that was not auto-rotated.
  • Server clock drifted forward past notAfter due to a broken NTP sync.

Detect this on your site

Run a quick scan with the SSL Checker. The tool surfaces this exact issue with the records and context needed to apply the fix below.

Open SSL Checker

How to fix it

  1. 1

    Confirm the expiration with openssl

    Run openssl s_client -servername example.com -connect example.com:443 /dev/null | openssl x509 -noout -dates to print notBefore and notAfter for the leaf served on the wire. This is authoritative — do not trust browser caches or CDN dashboards until the s_client output agrees.

  2. 2

    Renew via certbot if you use Let's Encrypt

    On the origin, run sudo certbot renew --force-renewal to bypass the 30-day window check. If renewal fails, run sudo certbot renew --dry-run and read the error — most failures are HTTP-01 challenge 404s caused by a reverse proxy that does not pass through /.well-known/acme-challenge/.

  3. 3

    Reissue from your cloud provider if you use a managed CA

    In AWS Certificate Manager, request a new public certificate, validate via DNS, then update the ALB or CloudFront listener to point at the new ARN. In Cloudflare, toggle the Edge Certificate off and back on to force reissuance from their backend CA. Caddy renews automatically; if it has not, check caddy reload logs for ACME errors.

  4. 4

    Reload the web server, do not just restart

    Run sudo nginx -t && sudo systemctl reload nginx or sudo apachectl -t && sudo systemctl reload apache2. A reload picks up the new certificate without dropping in-flight TLS sessions. HAProxy needs sudo systemctl reload haproxy. Caddy reloads automatically on file change but you can force it with sudo caddy reload.

  5. 5

    Verify in a clean browser context

    Open the site in a private window, click the lock icon, view the certificate, and confirm the new notAfter date is months in the future. Then run the SSL Checker at /ssl to verify the chain, OCSP status, and that no intermediate is expired either.

  6. 6

    Wire up renewal monitoring

    Add an external uptime probe that alerts at 21 days before expiry — far enough out that you have time to fix a broken ACME challenge without a panic page. Internal cron logs are not enough; the renewal can silently fail and the certificate still serve until the day it dies.

Example

# Force renewal and reload nginx in one step
sudo certbot renew --force-renewal --deploy-hook "systemctl reload nginx"

# Verify the new expiry
openssl s_client -servername example.com -connect example.com:443 </dev/null 2>/dev/null \
  | openssl x509 -noout -dates -subject -issuer

Force a fresh issuance from Let's Encrypt and reload nginx atomically

Frequently asked

Immediately as long as you reload (not just restart) the web server and the new certificate is served on the listener. CDNs like Cloudflare and Fastly may cache the old leaf for 5 to 15 minutes at edge POPs; if you cannot wait purge the edge or toggle 'Always Use HTTPS' off and on.

No. Let's Encrypt issues 90-day certificates and there is no extension API. You must reissue. The good news is reissuance with certbot takes about 10 seconds once the ACME challenge resolves correctly.

There is no protocol grace period. The instant the local system clock passes notAfter every standards-compliant client will reject the certificate. Some embedded HTTP clients ignore expiry but treating that as a feature is a security bug.

Related fixes