Guides · Tooling & Windows gotchas · Published 2026-09-13 · 3 min read

Node "unable to verify the first certificate" Behind Corporate Antivirus or a Proxy — Diagnose the Intercepting CA and Fix It With --use-system-ca, Not NODE_TLS_REJECT_UNAUTHORIZED

When npm, fetch or gcloud fail TLS verification on one machine only, something is intercepting HTTPS and re-signing it. How to see whose certificate you are being handed, why Node ignores the Windows store by default, and the one flag that fixes it safely.

The error looks like a bug in your code or a broken package registry: UNABLE_TO_VERIFY_LEAF_SIGNATURE, unable to verify the first certificate, or CERTIFICATE_VERIFY_FAILED from a Python or gcloud tool. It happens on one machine, or in one shell, and the same command works elsewhere. Almost always the cause is that something on the machine is sitting between you and the internet, decrypting HTTPS and re-signing it with its own certificate. Antivirus web shields do this on Windows; corporate proxies do it on managed laptops.

Confirm who is intercepting

Ask the server for its certificate and read the issuer. With OpenSSL:

openssl s_client -connect registry.npmjs.org:443 -servername registry.npmjs.org </dev/null 2>/dev/null | openssl x509 -noout -issuer

If the issuer is the real certificate authority, there is no interception and the problem is elsewhere. If it names your antivirus vendor or your company's proxy, you have found it. The same check from a browser, by clicking the padlock, shows the same issuer, and the browser is happy because the interceptor installed its root certificate in the operating system's trust store.

Why Node fails when the browser works

Browsers and most Windows-native tools use the operating system's certificate store, which contains the interceptor's root. Node ships its own bundled list of root certificates and, by default, ignores the system store. So Node sees a chain signed by an unknown root and refuses. Tools that bundle their own roots, such as gcloud's Python runtime, fail for the same reason.

The safe fix

Tell Node to also trust the system store:

node --use-system-ca script.js

Or for every Node process in the shell, including npm:

export NODE_OPTIONS=--use-system-ca

Verification still happens; Node simply trusts the roots your operating system trusts. Recent Node versions support this flag on Windows and macOS. The older alternative, NODE_EXTRA_CA_CERTS pointing at the interceptor's exported root certificate, also works and is the option for Node versions without the flag.

The fix to avoid

NODE_TLS_REJECT_UNAUTHORIZED=0 disables verification entirely for every connection. It makes the error go away and also makes every HTTPS request from that process trivially interceptable by anyone. It is never the right answer outside a throwaway experiment.

Where else this bites

Common mistakes

Summary

Read the certificate issuer to confirm interception, then run Node with --use-system-ca (or NODE_EXTRA_CA_CERTS) so it trusts the same roots the operating system does. Never turn verification off. The related Windows line-ending pitfalls for the same kind of "works on my machine" bug are in Git CRLF line endings on Windows with Node.

Related guides