• Node.js
  • Exact text checked with the real tool

CERT_HAS_EXPIRED

Also seen in: npm, yarn, axios, fetch (undici), Electron

In one sentence

A certificate in the server's chain is past its end date by your machine's clock: usually the server's certificate was not renewed or reloaded; sometimes the clock is wrong.

Where you see it

In Node.js when you call an HTTPS server with https.request, fetch, axios or another client, and in npm and yarn. The code comes from OpenSSL’s verify error 10; Node.js lists it in its TLS documentation as “Certificate has expired”.

TypeError: fetch failed
    at node:internal/deps/undici/undici:13510:13
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5) {
  [cause]: Error: certificate has expired
      at TLSSocket.onConnectSecure (node:_tls_wrap:1679:34)
      at TLSSocket.emit (node:events:507:28)
      at TLSSocket._finishInit (node:_tls_wrap:1078:8)
      at ssl.onhandshakedone (node:_tls_wrap:864:12) {
    code: 'CERT_HAS_EXPIRED'
  }
}

With https.get or axios, the same Error: certificate has expired with code: 'CERT_HAS_EXPIRED' is the error itself; with fetch, look in [cause]. npm prints:

npm error code CERT_HAS_EXPIRED
npm error errno CERT_HAS_EXPIRED
npm error request to https://registry.example.com/left-pad failed, reason: certificate has expired

Older npm versions start these lines with npm ERR!. A certificate that is not valid yet gives CERT_NOT_YET_VALID (“certificate is not yet valid”).

What it means

Every certificate has a validity period: a notBefore and a notAfter date. Node.js checks every certificate in the certificate chain against the machine’s clock. For one of them, the current time is past notAfter, so Node.js stops the connection, even if everything else is correct. Usually the server’s own certificate expired; sometimes the client’s clock is wrong.

Likely causes

The server’s certificate expired

Confirm what the server sends right now:

openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
subject=CN=api.example.com
notBefore=Jan  1 00:00:00 2026 GMT
notAfter=Apr  1 00:00:00 2026 GMT

Fix: renew the certificate, install it with its full chain, and reload the server. If it is not your server, tell its owner; there is nothing safe to change on the client. For a Node.js server you run, the new files take effect only after the process restarts or calls server.setSecureContext() with them.

Public TLS certificates may be valid for at most 200 days since 15 March 2026, and for at most 47 days from 15 March 2029. Renew automatically and monitor what each endpoint serves: How to check when a certificate expires, and monitor it.

It was renewed, but the old certificate is still served

Common after a manual renewal or a failed reload. Compare the file with what the server sends:

openssl x509 -in /etc/ssl/api.example.com/fullchain.pem -noout -enddate

If the file is newer, reload or restart the service. Behind a load balancer, test each backend directly, because one node may still have the old certificate. In Kubernetes, check which Secret the ingress really uses; see How to automate certificates in Kubernetes with cert-manager.

An intermediate or old cross-signed certificate in the chain expired

If the server’s own certificate is valid, look at the other certificates it sends:

openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null | grep -E '^ [0-9]+ s:|^   i:|NotAfter'

An expired intermediate left in the chain file must be replaced with the CA’s current chain (How to build the full chain file your server needs). Old clients can also build a path through an expired cross-signed certificate even when a valid path exists, as happened when DST Root CA X3, the older root that had cross-signed Let's Encrypt, expired, so devices that did not trust ISRG Root X1 began rejecting Let's Encrypt certificates (30 September 2021). Update Node.js on such clients.

The client’s clock is wrong

If a certificate that works everywhere else fails on one machine or container, check that machine’s time in UTC:

date -u
timedatectl status

Turn on time synchronisation (sudo timedatectl set-ntp true on systemd Linux, w32tm /resync on Windows). Virtual machines restored from snapshots and devices without a clock battery are typical.

Check yours

Try it: Certificate decoderOpen the full tool

Runs in your browser. Nothing you paste is sent anywhere.

PEM text, several PEM blocks, or a .pem, .crt, .cer or .der file (drop it on the box). Certificates are public, so they are safe to paste.

Paste the server’s certificate (the first BEGIN CERTIFICATE block from openssl s_client -showcerts). The decoder shows the validity dates and how many days are left or overdue.

Learn why

In the gameLevel 8: The certificateRoyal Notary, Keystone. In development

Early edition. Checked against the standards; an independent expert review is in progress. Found a mistake? Tell us.

Last reviewed Edition 0.9