TLScertificatesOpenSSL

How to debug TLS certificate expired errors

A practical TLS certificate expired guide that separates server certificate expiry, intermediate chain expiry, wrong SNI, stale load balancer certificates, client clock drift, and cached trust issues.

Published Jun 17, 2026 Updated Jun 17, 2026 Reviewed for incident use Commands included
Editorial note

This guide is maintained by Ops Error Atlas from a backend engineering perspective. It favors evidence, command output, and failure-layer separation over broad definitions or blind configuration changes.

How Ops Error Atlas reviews guides

certificate has expired means a TLS client rejected a certificate because its validity period does not include the client’s current time. The expired certificate may be the leaf server certificate, an intermediate certificate, or a different certificate served because SNI or load balancer configuration selected the wrong identity.

The useful question is:

Which certificate in the chain expired, and which listener served it to this client?

Do not renew a random certificate before proving what the client actually received.

Capture the served chain

Use the same hostname and port as the failing client:

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

Then inspect dates:

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

Also test with curl:

curl -v https://example.com/

Preserve:

  • hostname;
  • resolved IP;
  • port;
  • SNI value;
  • certificate subject;
  • issuer;
  • notBefore and notAfter;
  • verification error.

Leaf vs intermediate expiry

Common cases:

Expired itemSignal
leaf certificatesubject matches the site; notAfter is in the past
intermediate certificateleaf may look valid, but chain verification fails
wrong certificatesubject/SAN does not match the requested hostname
stale load balancer certdirect backend differs from public listener
client clock wrongcertificate is valid from external checks but one client rejects it

If only one client fails, check the client clock before assuming the server certificate is wrong.

Check SNI and resolved targets

SNI decides which certificate a TLS server may present.

Test each resolved IP while preserving SNI:

getent hosts example.com
openssl s_client -connect <ip>:443 -servername example.com -showcerts </dev/null
curl -v --resolve example.com:443:<ip> https://example.com/

Strong signals:

  • one IP serves an old certificate;
  • default virtual host serves an expired certificate when SNI is missing;
  • direct IP test differs from hostname test;
  • CDN or load balancer edge nodes are inconsistent.

Fix path:

  • update the certificate on every serving endpoint;
  • verify SNI configuration;
  • remove stale targets;
  • purge or rotate edge/load balancer certificates where applicable.

Check client time

Certificate validation depends on client time.

On Linux:

date -u
timedatectl status

Strong signals:

  • only one host, container, VM, or embedded device fails;
  • the client time is far in the past or future;
  • NTP is disabled or blocked;
  • logs have impossible timestamps.

Fix path:

  • restore time synchronization;
  • verify UTC time;
  • re-run the same TLS command after clock correction.

Do not disable certificate verification because one machine’s clock is wrong.

Load balancer and proxy branch

TLS may terminate at several places:

client -> CDN -> load balancer -> Nginx -> app

Each TLS termination point can have a different certificate.

Check:

  • CDN certificate;
  • load balancer listener certificate;
  • Nginx ssl_certificate;
  • service mesh gateway certificate;
  • backend mTLS certificate, if used;
  • renewal automation scope.

Nginx:

nginx -T | grep -n "ssl_certificate"

Kubernetes:

kubectl get secret -A | grep -i tls
kubectl describe ingress <name>

The public certificate and internal mTLS certificate are separate. Renewing one does not renew the other.

Renewal automation failures

Strong signals:

  • the certificate expired near a scheduled renewal window;
  • DNS-01 or HTTP-01 validation failed;
  • cert-manager, ACME client, or cron job logs show errors;
  • renewal succeeded on one node but not another;
  • new certificate exists on disk but the service did not reload.

Checks:

ls -l /etc/letsencrypt/live/<name>/
systemctl list-timers
journalctl -u certbot --since -7d

For Kubernetes cert-manager:

kubectl describe certificate <name>
kubectl describe certificaterequest <name>
kubectl logs -n cert-manager deploy/cert-manager --tail=100

Fix path:

  • fix the validation failure;
  • renew the certificate;
  • reload or restart the TLS terminator;
  • verify from outside the deployment network.

What not to do

  • Do not run curl -k and call the incident solved.
  • Do not renew only the certificate file if the load balancer still serves an old certificate.
  • Do not test by IP without preserving SNI and assume the result matches users.
  • Do not ignore intermediate certificate expiry.
  • Do not assume all CDN/load balancer edges update at the same moment.
  • Do not skip client clock checks when only one client fails.

Decision tree

certificate expired
|
+-- which cert expired?
|   +-- leaf -> renew serving cert
|   +-- intermediate -> fix chain bundle
|
+-- does every resolved IP serve same cert?
|   +-- no -> stale target or edge
|
+-- does SNI change served cert?
|   +-- yes -> fix virtual host/SNI config
|
+-- only one client fails?
|   +-- yes -> check client clock and trust store
|
+-- renewed cert exists but users still fail?
    +-- reload TLS terminator or update load balancer/CDN

Minimal incident note

hostname:
port:
client error:
client time:
resolved IPs:
SNI used:
served leaf subject:
served issuer:
notBefore:
notAfter:
expired cert in chain:
TLS termination point:
renewal automation status:
fix:
verification command:

The incident is solved when the client receives a complete valid chain from the intended listener and the verification command succeeds without disabling certificate checks.

References

Related errors

Move laterally when the first symptom points to adjacent network failures.

Related guide TLS / certificates

How to fix "certificate verify failed"

A practical certificate verify failed guide that separates hostname mismatch, expired certificates, incomplete chains, missing trust stores, containers, and runtime-specific CA behavior.

Read guide
Related guide TLS / certificates

What causes TLS handshake failure

A practical TLS handshake failure guide that separates certificate chain problems, SNI mismatch, protocol and cipher mismatch, mTLS failures, and proxy TLS termination mistakes.

Read guide
Related guide TLS / OpenSSL

How to fix "SSL wrong version number"

A practical TLS wrong version number guide that separates HTTP-to-HTTPS mismatches, TLS termination mistakes, proxy upstream schemes, STARTTLS confusion, and port mixups.

Read guide
Before applying a fix

Keep one representative log line, the failing source and destination, the command output you used, and the verification command after the change. This makes the result reproducible and helps separate temporary recovery from a proven fix.

Browse related error guides