How to Fix "Unable to Verify the First Certificate" in Node.js
Confirm the gap with openssl, then repair the server's chain or add trust in Node.js.
Updated
TL;DR
UNABLE_TO_VERIFY_LEAF_SIGNATURE ("unable to verify the first certificate") means a server sent Node.js its leaf alone, without the intermediate that connects it to a trusted CA. The site's operator fixes it by sending the full chain; meanwhile, NODE_EXTRA_CA_CERTS can supply the missing intermediate.
What the error looks like in each client
One server that sends no intermediate, as each tool reports it; Python's version is on the CERTIFICATE_VERIFY_FAILED page.
| Where | What you see |
|---|---|
| Node.js 24 http and https (code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') | Error: unable to verify the first certificate; if the root CA is installed locally, try running Node.js with --use-system-ca |
| Node.js 20 and earlier | Error: unable to verify the first certificate |
| Node.js 20 fetch() | TypeError: fetch failed ... [cause]: Error: unable to verify the first certificate |
| npm 11 | npm error code UNABLE_TO_VERIFY_LEAF_SIGNATURE |
| curl | curl: (60) SSL certificate problem: unable to get local issuer certificate |
| git | fatal: unable to access 'https://localhost:48466/repo.git/': SSL certificate problem: unable to get local issuer certificate |
Why this happens
Browsers find a missing certificate on their own, and Node.js does not.
Node.js builds the path to a trusted CA only from what the server sends plus its own CA list. Chrome downloads the missing piece from a link in the leaf, so the page opens there while your script fails.
Diagnose your UNABLE_TO_VERIFY_LEAF_SIGNATURE first
Two browser checks and three openssl readings separate a server mistake from interception on your network.
Open the URL in Chrome; if it loads without a warning, the certificate is valid and only the delivered chain falls short.
Test the host name on SSL Labs; if it reports an incomplete chain, the fault sits on the server, not your network.
Run openssl s_client -connect host:443 -servername host -showcerts; if Certificate chain shows only entry 0, nothing beyond the leaf arrived.
Read entry 0's i: line; if it names your employer or a filtering product, not a public CA, your network inspects HTTPS.
Compare entry 0's i: line with entry 1's s: line; if they differ, the server ships a wrong or outdated intermediate.
Solutions ranked by effectiveness
Ranked by reach: a server change fixes everyone, the other two only your own code.
- Most common fix
Send the full chain from the server
Applies when the server is yours or you can reach its admin; send them your openssl output. Put the leaf first in the file and its issuers after it, leaving out the root.
With Certbot, point nginx ssl_certificate or Apache SSLCertificateFile at fullchain.pem, not cert.pem.
With another CA, paste its bundle below your certificate in one file, leaf first.
In a Node.js https server, pass that combined file as the cert option.
- Check next
Add the intermediate with NODE_EXTRA_CA_CERTS
Applies when the server is out of your hands. The variable adds a PEM file to the CAs that every request in the Node.js process trusts.
Download the intermediate from the CA Issuers link in the leaf's Authority Information Access field.
Convert a binary .cer file: openssl x509 -inform DER -in ca.cer -out intermediate.pem
Start Node.js with NODE_EXTRA_CA_CERTS=./intermediate.pem; changing process.env at runtime has no effect.
- For developers
Trust it in one agent only
Applies when only one client should get the extra trust; axios accepts the same agent as httpsAgent. Setting ca replaces Node's default list, so the code adds the bundled roots back.
const fs = require("node:fs"); const https = require("node:https"); const tls = require("node:tls"); const ca = [...tls.rootCertificates, fs.readFileSync("intermediate.pem", "utf8")]; const agent = new https.Agent({ ca }); https.get("https://api.example.com/", { agent }, (res) => { console.log("HTTP", res.statusCode); res.resume(); }).on("error", (err) => console.log(err.code, err.message));
Stop the UNABLE_TO_VERIFY_LEAF_SIGNATURE from coming back
Habits for teams that run HTTPS servers and for scripts that depend on servers run by others.
After each renewal, test the site from Node.js rather than a browser, which may already hold the intermediate.
Take the chain your CA issues with each renewal; Let's Encrypt may switch intermediates without warning.
Add a chain check to your deploy pipeline, so a config that loses the intermediate never ships.
Note why each NODE_EXTRA_CA_CERTS file exists, and delete it after the site owner repairs the chain.
Related errors
Learn more
FAQ
Proxy usage your scripts can check
Geonode's usage endpoint returns the bandwidth you've used, so a scheduled job can log it.


