Skip to content
Issue docs

SSL certificate is outside its validity window

Criticalssl_certificate_expiredIssue 39

What is this issue?

This issue fires when the certificate is outside its validity window — either past its notAfter date or not yet at its notBefore date. The finding records both dates and the issuer.

Under the old combined check this was the condition the title named — not served over HTTPS, or certificate expired — which meant every other TLS failure was reported as an expiry problem too. Now it means only what it says.

It is reported separately from SSL certificate is rejected by browsers because the fix is different in kind: this certificate is the right one, correctly installed, and needs renewing rather than replacing.

Why it matters

Browsers show a full-page interstitial (NET::ERR_CERT_DATE_INVALID) on every page of the site, and the transition is abrupt — the site is fine one minute and blocked the next.

  • Nobody reaches the content. Traffic drops to approximately zero for the duration.
  • Googlebot stops fetching, so a prolonged outage costs index freshness on top of the traffic.
  • The not-yet-valid case is a real one. A certificate installed ahead of its start date, or a server whose clock is wrong, fails in exactly the same way and confuses everyone looking at it, since the certificate "looks fine".

How to fix it

  1. Renew immediately. With Let's Encrypt: certbot renew --force-renewal, then reload the server.

  2. Reload the server after renewal. A renewed file on disk is not a renewed certificate in memory — this is the most common reason a "renewed" certificate is still served expired.

  3. Automate it. Set renewal to run at roughly a third of the certificate lifetime remaining (30 days for a 90-day certificate) so a failed attempt has room to retry.

  4. Check the server clock if the certificate is reported as not yet valid. Enable NTP.

  5. Monitor expiry independently of the renewal job, so a silently failing cron is caught before the certificate lapses.

  6. Check every layer — CDN, load balancer and origin each carry their own certificate and their own expiry.

Examples

1. A lapsed certificate

Problem — the certificate stopped being valid nine days before the scan, so every visitor gets a full-page interstitial before the site loads:

Issuer:       Let's Encrypt R3
Valid from:   2026-05-16
Valid until:  2026-08-14
Scanned on:   2026-08-23

Fixed — renew and reinstall:

Issuer:       Let's Encrypt R3
Valid from:   2026-08-14
Valid until:  2026-11-12

Renewal is the whole fix. The certificate is the right one for this hostname and it is correctly installed — which is why this is reported apart from a certificate browsers reject outright, where the fix is to replace it.

2. A certificate that is not yet valid

Problem — the certificate was installed ahead of its start date, or the server's clock is wrong. The browser rejects it exactly as it rejects an expired one:

Valid from:   2026-09-01
Valid until:  2026-12-01
Scanned on:   2026-08-23

Fixed — wait for the start date, or issue one valid from today, and correct the server clock via NTP if that was the cause.

3. A certificate that could not be read is not reported

Not reported — the probe timed out:

Issuer:       null
Valid from:   null
Valid until:  null

Nothing is raised. The page was fetched successfully over HTTPS, and a failed probe means the question could not be asked. Reporting it as an expiry would send someone to check a date that is fine — and the finding would disappear on the next scan.

How PixyScan detects this

  1. Certificate probe. For each HTTPS page, the crawler reads the certificate's notBefore and notAfter dates and its issuer.

  2. Window comparison. The issue is raised when notBefore is in the future or notAfter is in the past, at scan time.

  3. Requires a readable certificate. All three of issuer, validFrom and validUntil must have been read; a failed probe produces no finding, since being unable to read a certificate is not evidence that it is expired.

  4. Precedence. Evaluated last, so it fires only when the scheme is HTTPS and the certificate validates — handshake, hostname and trust chain all sound — and the dates alone are wrong. Anything that stops the certificate validating is reported as SSL certificate is rejected by browsers instead.

  5. Scope. Page-level.

What we store

Storage Level

Page Level — evaluated for each crawled HTTPS URL.


Database Table / Prisma Model

PageSecurityHeader

One row per crawled URL, keyed by urlId.


Fields Used

Field Type Description
sslCertificateIssuer String? The certificate authority that signed the certificate, as read from the handshake
sslValidFrom DateTime? The certificate's notBefore date
sslValidUntil DateTime? The certificate's notAfter date — the one that has usually passed

All three must be non-null for this issue to be raised. A probe that timed out or was refused leaves them null, and being unable to read a certificate is not evidence that the certificate is expired, so no finding is written in that case.

Both dates are stored, not just the expiry. The check covers a certificate that is not yet valid as well as one that has lapsed — a clock skew or a certificate installed ahead of its start date fails in exactly the same visible way — and the report names the window rather than asserting a single date.


Detection Dependencies

  • HTTP Response — the page must have been fetched over https:; a plain-HTTP page is #38 instead
  • TLS Certificate — a successful handshake from which the issuer and both validity dates can be read

Further reading