← Blog

TLS certificates now last 200 days, Let's Encrypt's soon 64: what to check

Short answer: since 15 March 2026 a public TLS certificate (still widely called an SSL certificate) can't be issued for longer than 200 days; it used to be 398. In March 2027 the cap drops to 100 days, and in March 2029 to 47. From 10 February 2027 Let's Encrypt issues 64-day certificates by default instead of 90-day ones. If renewal runs on its own and your server picks up the new certificate, there's nothing to do. Trouble starts where certificates are replaced by hand or renewed on a fixed cron schedule, and where the server isn't reloaded after a renewal.

The cap for any public certificate authority, and Let's Encrypt's default lifetime

What changed, and why#

Lifetimes are set by the CA/Browser Forum, the body of certificate authorities and browser makers. In April 2025 it passed ballot SC-081v3: 25 certificate authorities and all four browsers voted for it, five abstained, nobody voted against. The cap comes down in three steps:

FromMaximum certificate lifetime
until 15 March 2026398 days
15 March 2026200 days
15 March 2027100 days
15 March 202947 days

A stolen key stays useful until the certificate expires, and a shorter lifetime narrows that window. It also lets the industry move to new algorithms faster. The rule applies to newly issued certificates. Existing ones run out their term; nobody revokes them.

Let's Encrypt is ahead of the schedule. Its announcement of 7 October 2026 says that from 10 February 2027 the default certificate lasts 64 days, the last 90-day certificate should expire around 11 May 2027, and 45 days are planned for 2028. Staging has been issuing 64-day certificates since 14 October 2026.

Where it breaks#

Certificates installed by hand. If you renew by hand, a yearly reminder is no longer enough: it's twice a year now, four times from 2027 and about eight times from 2029. At that pace a manual renewal is easy to miss.

Renewal on a fixed schedule. Running certbot "every 60 days" left a month of slack for a 90-day certificate. For a 64-day one the slack shrinks to four days, and a single failed renewal attempt becomes an expired certificate.

The same day-60 cron: a month of slack for a 90-day certificate, four days for a 64-day one

Renewed, but the server serves the old one. The new file is on disk, but nginx, the load balancer or the app keeps the old certificate in memory until it's reloaded.

The certificate lives in several places. A load balancer, a CDN, a Kubernetes secret, a copy on a second server. If the update doesn't reach all of them, some requests go out with an expired certificate.

Certificate pinning in an app. A mobile app or client script that checks the fingerprint of the server's exact certificate breaks on every renewal. If you need pinning, pin the public key or the intermediate certificate, and keep a backup pin anyway.

The CA/Browser Forum rules don't cover certificates from a company's private CA. They don't cover Russia's national CA either: it isn't publicly trusted, and browsers other than Yandex Browser don't trust it by default. The Russian Trusted Root CA post explains why an API behind such a certificate fails in curl and how to fix it.

Setting up Let's Encrypt renewal#

  1. An ACME client with ARI support. ARI (ACME Renewal Information) lets the certificate authority tell the client when to renew. Let's Encrypt says clients with ARI need no changes. Check your client's documentation for ARI support.
  2. Renewal by fraction of lifetime, not by calendar. Let's Encrypt recommends renewing at about two thirds of the lifetime: day 43 for a 64-day certificate. Search cron jobs, wrapper scripts and runbooks for 60, 80 and 83.
  3. A reload after renewal. certbot has --deploy-hook for this, and other clients have similar hooks. Something like systemctl reload nginx should run automatically after every successful renewal.
  4. An alert when renewal fails. Don't rely on emails from the certificate authority: Let's Encrypt, for one, stopped sending expiry reminders in 2025.
  5. A dry run in staging. While there's time before February, issue a test certificate from Let's Encrypt staging and check the whole chain: issuance, installation, reload, and what the server sends. The staging directory is https://acme-staging-v02.api.letsencrypt.org/directory; in certbot, use --test-cert.

How to check a certificate's expiry date on the server#

The file on disk can be fresh while the server keeps sending the old certificate. So check what actually comes over the network:

Check the certificate that arrives over the network, not the file on disk
echo | openssl s_client -connect api.example.com:443 \
  -servername api.example.com 2>/dev/null \
  | openssl x509 -noout -dates -issuer

notAfter is the end of the validity period: after that moment browsers and clients reject the certificate. Keep -servername: recent OpenSSL fills it in from -connect, but when you connect by IP or run an older build, a server hosting several sites hands you another domain's certificate without it.

curl -v https://api.example.com shows the same in its start date and expire date lines. If the certificate has already expired, curl exits with code 60 and, depending on the build, says SSL certificate problem: certificate has expired or the generic SSL peer certificate or SSH remote key was not OK.

For monitoring, turn the same command into a condition: with -checkend, openssl exits non-zero if the certificate expires within the given number of seconds. 14 days is 1209600:

echo | openssl s_client -connect api.example.com:443 \
  -servername api.example.com 2>/dev/null \
  | openssl x509 -noout -checkend 1209600

In an API client, it helps to keep one check request to your service and send it after every renewal. Certificate verification can't be switched off in Tetiva, so a request to a server with an expired or mismatched certificate ends in an error.

The certificate has already expired: what to do#

Chrome shows NET::ERR_CERT_DATE_INVALID, Firefox SEC_ERROR_EXPIRED_CERTIFICATE, and curl exits with code 60.

  1. Check what the server sends with the command above. If notAfter is in the past but the file on disk is fresh, the server wasn't reloaded: systemctl reload nginx or its equivalent is enough.
  2. If there's no fresh file, renew by hand: run sudo certbot renew and read the error. Common causes: port 80 is closed to the HTTP-01 challenge, the domain's DNS points at another server, or the DNS provider's API token used for DNS-01 has expired.
  3. If the server sends a valid certificate but only one user sees the error, check the clock on their device: when the date runs ahead, the client treats the certificate as expired.

If the connection drops before a certificate arrives, that's a different problem: finding the layer where the request died.

Cheat sheet#

  • Until 10 February 2027: 200 days at most for any public CA, 90 at Let's Encrypt.
  • From 10 February 2027: Let's Encrypt issues 64 days. From 15 March 2027: the cap is 100 days.
  • Set up: ARI or renewal at two thirds of the lifetime, a reload after renewal, an alert on failure.
  • Check: openssl s_client ... | openssl x509 -noout -dates against the network address, not the file.