# NET::ERR_CERT_DATE_INVALID: causes and fixes

> Chrome shows NET::ERR_CERT_DATE_INVALID when the certificate expired or the visitor's clock is wrong. How to tell which, renew it and get warned in time.

`NET::ERR_CERT_DATE_INVALID` is Chrome's and Edge's error for a certificate that isn't valid on today's date: it expired, or it isn't valid yet. Firefox calls the same thing `SEC_ERROR_EXPIRED_CERTIFICATE`, Safari says the certificate "has expired". Either the site's certificate really is outside its dates, or the visitor's computer has the wrong date.

## Is it the site or the visitor?

Check the certificate from another machine. If it's expired there too, the problem is the server:

```bash
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates
```

Replace `example.com` with the host. A healthy certificate:

```text
subject=CN=lampwick.dev
issuer=C=US, O=Let's Encrypt, CN=YE1
notBefore=Nov  7 13:17:13 2026 GMT
notAfter=Feb  5 13:17:12 2027 GMT
```

An expired one, tested against `expired.badssl.com`:

```text
notBefore=Apr  9 00:00:00 2015 GMT
notAfter=Apr 12 23:59:59 2015 GMT
```

`curl https://example.com/` says `curl: (60) SSL certificate problem: certificate has expired` for the same case. If `notAfter` is in the future and only one visitor sees the error, it's on their side.

## Fixes on the visitor's side

- **The device clock is wrong.** The most common visitor-side cause: a PC with a dead CMOS battery, a phone set to manual time, a virtual machine restored from a snapshot. Turn on automatic date and time and reload.
- **Antivirus or a company proxy intercepts HTTPS.** It re-signs every site with its own certificate; when its own clock or certificate is off, every site fails. Test on another network.
- **A very old device.** It doesn't know newer root certificates and may report an expired chain. Updating the OS or browser is the only real fix.

If you run the site, none of these are yours to fix, but check the server first: a visitor reporting this is often the first sign that renewal failed.

## Fixes on the server: the certificate expired

Certificates expire on purpose, and they're getting shorter. The CA/Browser Forum voted in April 2025 ([ballot SC-081v3](https://cabforum.org/2025/04/11/ballot-sc081v3-introduce-schedule-of-reducing-validity-and-data-reuse-periods/)) to cut the maximum lifetime of public TLS certificates from 398 days to 47 days between March 2026 and March 2029: 200 days from 15 March 2026, 100 days from 15 March 2027, 47 days from 15 March 2029 ([DNSimple's summary](https://support.dnsimple.com/articles/announcement-ssl-certificate-validity-changes/)). Let's Encrypt issues 90-day certificates today and moves its default to 64 days on 10 February 2027 and 45 days on 16 February 2028 ([Let's Encrypt](https://letsencrypt.org/2025/12/02/from-90-to-45/)). All checked on 10 Nov 2026. Renewing by hand once a year no longer works: renewal has to be automatic, and you need to know when the automation breaks.

Renew now with certbot, then find out why it didn't renew on its own:

```bash
sudo certbot certificates
sudo certbot renew --dry-run
sudo certbot renew
```

`certbot certificates` lists each certificate with its expiry date. `--dry-run` tests renewal against Let's Encrypt's staging server without changing anything, and shows the error if there is one.

### Renewal isn't scheduled

certbot installs a systemd timer or a cron job, and a server rebuilt from a script or a container without it never renews. Check:

```bash
systemctl list-timers | grep certbot
```

No line means no schedule. With the snap package, `sudo systemctl enable --now snap.certbot.renew.timer`; otherwise add a cron entry that runs `certbot renew` twice a day.

### The challenge fails

Let's Encrypt has to reach your server to prove you control the domain. `--dry-run` shows which part fails:

- **Port 80 is closed.** HTTP-01 validation always starts on port 80, even when the site only serves HTTPS. Open it in the firewall and security group.
- **A redirect or a new deploy broke `/.well-known/acme-challenge/`.** A changed web root, a catch-all route in your app, or a CDN that doesn't pass the path through. With certbot's `--webroot` mode, the web root must still be the directory the server serves.
- **DNS moved.** The domain now points at a new server or a CDN, so the challenge never reaches the server that runs certbot. Renew where the traffic goes, or use DNS-01 validation.

### The certificate renewed, but the server still serves the old one

Nginx, Apache and HAProxy read the certificate at start and keep it in memory. If nothing reloads them after renewal, the new file sits on disk while visitors get the expired one. Add a deploy hook, which certbot runs after every successful renewal:

```bash
printf '#!/bin/sh\nsystemctl reload nginx\n' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx
```

Then check what the server actually serves with the `openssl s_client` command above, not the file on disk.

### Another server or the CDN has its own certificate

Behind a load balancer or CDN, the visitor sees the certificate of whatever terminates TLS. One node with an old certificate gives the error to some visitors only. An origin certificate behind Cloudflare that expired gives Cloudflare error 526 instead. Check each node by its own IP:

```bash
echo | openssl s_client -connect 203.0.113.10:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate
```

`203.0.113.10` is the node's IP; `-servername` still sends the real host name.

### Hosted platforms

On Vercel, Netlify, Lovable, Laravel Cloud, Forge and similar, the platform renews for you, as long as the domain's DNS still points at it. Renewal usually breaks when DNS was changed or a CAA record doesn't allow the platform's certificate authority. Check the domain's status in the platform's dashboard.

## Get warned before it expires

Since 4 June 2025 Let's Encrypt no longer emails you before a certificate expires ([Let's Encrypt](https://letsencrypt.org/2025/01/22/ending-expiration-emails/), checked 10 Nov 2026). If renewal breaks, the first warning is often a visitor's screenshot of this error.

Every Lampwick [uptime monitor](/docs/uptime/) also checks its URL's certificate from outside, every 6 to 12 hours. It alerts the team 14, 7, 3 and 1 days before expiry, and once when the certificate becomes invalid: expired, for the wrong host or from an unknown issuer. Because it reads the certificate the server actually serves, it also catches the "renewed but not reloaded" case. The details are in [SSL, domain and DNS checks](/docs/host-checks/#ssl-certificate); [SSL certificate monitoring](/features/ssl-monitoring/) has the overview.

For the other reasons a site goes offline, such as an expired domain or a DNS change, see [how to know when your app is down](/guides/know-when-your-app-is-down/).
