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

Error message

unable to verify the first certificate

Also printed as

  • code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'
  • UNABLE_TO_VERIFY_LEAF_SIGNATURE

Recent Node.js releases (24.22 in our test) append "; if the root CA is installed locally, try running Node.js with --use-system-ca" to this message. With fetch() it appears as the cause of "TypeError: fetch failed"; axios and got show it as the error message with the same code.

Node.js tls / https / fetch — code UNABLE_TO_VERIFY_LEAF_SIGNATURE (OpenSSL X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, 21)

What it means

Node.js received only the server's own (leaf) certificate and could not find its issuer anywhere: not in the handshake and not among its trusted roots. Without the issuer it cannot even check the leaf's signature, hence "the first certificate". Almost always the server forgets to send its intermediate certificate; browsers mask this by downloading intermediates, Node.js never does.

Why it happens

How to fix it

  1. 1.

    Check how many certificates the server sends

    If only depth 0 appears and its issuer is an intermediate CA, the fix belongs on the server.

    bash
    openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
      | grep -E '^ *[0-9]+ s:|^ *i:'
    # Only " 0 s:" present, with an i: that is an intermediate -> the server must send that intermediate
  2. 2.

    Serve the full chain

    Give the server a bundle with the leaf followed by every intermediate. For Node.js servers that is the cert option; for nginx ssl_certificate, for cloud load balancers the certificate chain field.

    js
    // Node.js HTTPS server: pass leaf + intermediates as the cert
    import https from "node:https";
    import fs from "node:fs";
    
    https.createServer({
      key: fs.readFileSync("server.key"),
      cert: fs.readFileSync("fullchain.pem"),   // leaf first, then intermediate(s)
    }, handler).listen(443);
  3. 3.

    If you cannot change the server: add the intermediate on the client

    NODE_EXTRA_CA_CERTS accepts intermediate certificates too and is read once at process start. Prefer fixing the server: every other client (mobile apps, partners) has the same problem.

    bash
    # When you cannot fix the server: trust the missing intermediate on the client too
    NODE_EXTRA_CA_CERTS=/etc/ssl/certs/issuing-ca.pem node app.js
  4. 4.

    Temporary diagnostic: disable verification for one run

    NODE_TLS_REJECT_UNAUTHORIZED=0 turns off certificate checks for every TLS connection in the process and prints a warning. Use it only to confirm the diagnosis; in production it lets anyone on the path read and alter the traffic.

    bash
    # Temporary diagnostic only: disables certificate verification for the whole process
    NODE_TLS_REJECT_UNAUTHORIZED=0 node app.js

Related errors

Browse the full signature error database