Error message
npm error code UNABLE_TO_VERIFY_LEAF_SIGNATURE
Also printed as
- npm ERR! code UNABLE_TO_VERIFY_LEAF_SIGNATURE
- npm error request to https://registry.example.com/lodash failed, reason: unable to verify the first certificate
npm 10 and later print "npm error code UNABLE_TO_VERIFY_LEAF_SIGNATURE"; npm 9 and older "npm ERR! code UNABLE_TO_VERIFY_LEAF_SIGNATURE". Recent Node.js versions add "; if the root CA is installed locally, try running Node.js with --use-system-ca" to the reason line.
npm (Node.js) — registry request over HTTPS
What it means
The registry (or a proxy in front of it) sent only its own certificate, and Node.js could not find the certificate that issued it. Without the issuer, the leaf's signature cannot be checked at all. This is a server configuration problem: the intermediate certificate is missing from the handshake, which Node.js does not repair by downloading it.
Why it happens
A private registry configured with the leaf only
commonNexus, Artifactory, Verdaccio or a reverse proxy in front of them installed with cert.pem instead of the full chain.
A proxy that presents only its leaf
occasionalSome TLS-inspecting proxies send a certificate issued by an intermediate without including that intermediate.
A leaf from an unknown private CA
occasionalIf nothing above the leaf is known to Node.js, the same code appears; trusting the CA (and its intermediate) fixes it.
How to fix it
- 1.
Count the certificates in the handshake
Only depth 0 with an intermediate as issuer confirms the missing intermediate.
bash openssl s_client -connect registry.example.com:443 -servername registry.example.com -showcerts </dev/null 2>/dev/null \ | grep -E '^ *[0-9]+ s:|^ *i:' - 2.
Serve the full chain on the registry
Configure the TLS terminator in front of the registry with leaf plus intermediates. This fixes npm, yarn, pnpm, Docker and every other client at once.
text # Registry behind nginx: serve leaf + intermediate(s) ssl_certificate /etc/nginx/tls/registry.example.com.fullchain.pem; ssl_certificate_key /etc/nginx/tls/registry.example.com.key; - 3.
Workaround: trust the intermediate on the client
A cafile with the intermediate and its root lets npm verify the leaf until the server is fixed; cafile replaces the default roots for npm, so add public roots too if the same npm also uses registry.npmjs.org.
bash # Until the registry is fixed: trust the intermediate (plus its root) on the client cat issuing-ca.pem corp-root-ca.pem > /etc/ssl/private-ca/registry-ca.pem npm config set cafile /etc/ssl/private-ca/registry-ca.pem