Skip to content
ecdsa.com
Node.js tls / https / fetchNode.js crypto

Error message

certificate has expired

Also printed as

  • code: 'CERT_HAS_EXPIRED'
  • CERT_HAS_EXPIRED

The message text is the same in all Node.js versions; with fetch() it appears as the cause of "TypeError: fetch failed", npm prints "npm error code CERT_HAS_EXPIRED". Certificates that are not valid yet produce CERT_NOT_YET_VALID instead.

Node.js tls / https / fetch — code CERT_HAS_EXPIRED (OpenSSL X509_V_ERR_CERT_HAS_EXPIRED, 10)

What it means

A certificate in the chain Node.js built is past its notAfter date by this machine's clock. Usually it is the server's own certificate; it can also be an intermediate the server still sends, or a root from the outdated list bundled with an old Node.js release. Node.js reports the message "certificate has expired" with the code CERT_HAS_EXPIRED.

Why it happens

How to fix it

  1. 1.

    Read the served certificate's dates

    Connect once without verification, only to read the certificate: valid_to and authorizationError show which certificate expired. Do not send data over that connection.

    js
    // Read the dates of the served certificate (inspection only, nothing is sent)
    import tls from "node:tls";
    
    const socket = tls.connect({ host: "api.example.com", port: 443, servername: "api.example.com", rejectUnauthorized: false }, () => {
      const cert = socket.getPeerCertificate();
      console.log(cert.subject.CN, cert.valid_from, "->", cert.valid_to, "authorized:", socket.authorized, socket.authorizationError);
      socket.end();
    });
  2. 2.

    Renew and reload the server certificate

    Renew through the ACME client or CA, reload every process that terminates TLS, then confirm the new expiry from the network side.

    bash
    sudo certbot renew && sudo systemctl reload nginx
    echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null | openssl x509 -noout -enddate
  3. 3.

    Upgrade Node.js on old installations

    If the server's certificate is valid for other clients, an old Node.js root list is the likely culprit; upgrading to a maintained release refreshes the bundled roots.

    bash
    # Node.js bundles its root list: old releases carry old (or expired) roots
    node -v
    node -p "require('tls').rootCertificates.length"

Related errors

Browse the full signature error database