2026-11-01
Unable to verify the first certificate in Node.js: find the chain problem
- ssl
- tls
- nodejs
- certificates
- debugging
To fix “unable to verify the first certificate” in Node.js, inspect the chain the remote server sends and the CA certificates trusted by the failing process. A public server may be missing an intermediate; an internal service or inspecting proxy may require an approved private CA. Keep TLS verification enabled while fixing that chain or trust configuration.
The certificate chain guide explains the leaf, intermediate and root roles. This walkthrough focuses on identifying which part Node.js cannot verify.
Capture the error code from the failing process
The message alone can hide useful detail. Reproduce the connection inside the same container, VM or runtime environment that fails, with the same destination and proxy configuration.
For a direct HTTPS request using Node's standard library:
.;
Replace the example URL with your endpoint. This small script isolates a direct Node HTTPS connection; it does not automatically reproduce an application library's custom agent or proxy. Compare those settings if the script succeeds while your application fails.
Node's certificate error reference distinguishes these failures:
| Error | What to investigate |
|---|---|
UNABLE_TO_VERIFY_LEAF_SIGNATURE | A lone, non-self-signed leaf with no verifiable issuer in the presented chain |
UNABLE_TO_GET_ISSUER_CERT_LOCALLY | An issuer that the verifier cannot find locally |
DEPTH_ZERO_SELF_SIGNED_CERT | A self-signed leaf that this client does not trust |
ERR_TLS_CERT_ALTNAME_INVALID | A name mismatch, a separate problem from building the chain |
For the last case, use the hostname mismatch checklist. Installing extra CA certificates does not make a certificate cover an unrelated hostname.
Inspect the certificates the server actually sends
Run this from the failing environment if OpenSSL is available:
Read the certificate list and verification output separately. OpenSSL's s_client documentation describes -showcerts as the list sent by the server. It is not a reconstructed, verified chain.
Copy only the BEGIN CERTIFICATE through END CERTIFICATE blocks into the certificate decoder. It shows each supplied certificate's subject, issuer and dates, in pasted order. Check whether the leaf's issuer corresponds to the next certificate's subject. Matching those names is a useful clue, but it does not verify the signature or prove trust.
The public SSL checker offers another view of what a public host serves. It cannot reproduce a private network or your company's HTTPS inspection path.
If you control the public server, send its intermediates
For a public certificate, the usual deployment is the leaf first, followed by the required intermediate certificates. The client supplies a trusted root. Sending a root certificate from the server does not make the client trust it.
On an Nginx deployment using Certbot's standard file layout, the certificate directive normally points to the full chain:
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
Use your actual paths. Nginx's certificate-chain documentation describes the combined certificate file and its order. A load balancer or managed certificate service has a different configuration surface; fix the certificate at the place that terminates TLS.
After validating the configuration and deploying it through your normal process, repeat the request from the failing Node environment. Also inspect the public endpoint again. Reading a corrected file on disk does not establish that the running listener loaded it.
If the service uses a private CA, configure deliberate trust
An internal CA may be absent from Node's default roots. An HTTPS inspection proxy can also present certificates signed by your organization's CA. Compare the observed issuer with the expected infrastructure, and obtain the approved CA certificate from the people who administer it.
Node supports extending its roots at startup:
NODE_EXTRA_CA_CERTS=/etc/company/approved-root-ca.pem
The NODE_EXTRA_CA_CERTS documentation specifies that the file contains trusted PEM certificates, is read when Node starts, and does not update when you change process.env later. Restart the actual application process with that setting.
Also inspect any explicitly configured ca option in your HTTPS agent or TLS connection. Node documents that an explicit ca replaces the normal and extra CA certificates for that connection. Adding an environment variable will not override it.
Do not use a random certificate copied from the failed connection as a new trust anchor. Establish the issuer you intend to trust and fix the server's intermediate chain independently.
Compare browsers and Node without assuming either is wrong
A browser success does not prove that every client can build the same path. Your browser and Node process may use different root stores, cached intermediates, network routes or proxy certificates.
Compare four things: the actual hostname, the leaf fingerprint, the sent intermediate list and the configured trust roots. If the fingerprint or issuer differs between your laptop and the container, investigate the path before rewriting certificate validation code.
Keep rejectUnauthorized enabled. Setting it to false, or disabling verification process-wide, removes the check whose failure you are investigating and can accept an unintended peer.
Verify the fix from the environment that failed
Repeat the original application request after the server or trust configuration changes. Confirm that the error disappears with normal verification settings.
Then follow the certificate monitoring walkthrough. An expiry check gives advance notice of dates. An HTTPS check with TLS verification enabled catches validation failures from its own network and trust context. A private CA path still needs a check that can reach and trust that environment.
Frequently asked questions
What is UNABLE_TO_VERIFY_LEAF_SIGNATURE in Node.js?
It is a TLS certificate verification error. Node.js describes it as a chain containing only one certificate that is not self-signed, leaving no signature that can be verified. Inspect the certificates the server sends and the trust configuration of the failing Node.js process.
Why can a URL work in my browser but fail in Node.js?
The clients can have different trust stores, cached intermediates, proxy paths or certificate-chain behavior. Compare the chain and issuer from the failing environment before changing application code.
When does Node.js read NODE_EXTRA_CA_CERTS?
Node.js reads it when the process starts. The file contains trusted PEM certificates and extends the normal roots. Changing process.env later does not update the current process. An explicit TLS ca option replaces the normal and extra CA certificates for that connection.