π¨ The Problem
Suddenly, npm install started failing across all projects with:
SELF_SIGNED_CERT_IN_CHAIN
It wasnβt repo-specific.
It wasnβt npm registry downtime.
It wasnβt a corrupt cache.
It happened everywhere.
Environment:
- Node.js v22.x
- npm v10.x
- Corporate network (Zscaler SSL Inspection)
- Windows + Git Bash
π What Was Really Happening?
Our company uses Zscaler SSL Inspection, which performs TLS interception (MITM) for outbound HTTPS traffic.
So instead of the normal TLS chain:
registry.npmjs.org
β
Public CA
The actual chain becomes:
registry.npmjs.org (re-signed)
β
Zscaler Intermediate
β
Internal Enterprise CA
β
Enterprise Root CA
This is normal in enterprise environments.
β Why Did It Suddenly Break?
The key trigger was:
Node.js v22 tightened TLS validation behavior.
Older Node versions were more tolerant about incomplete chains.
Node 22:
- Uses OpenSSL 3.x
- Enforces stricter certificate validation
- Does not auto-reconstruct incomplete intermediate chains
Providing only the enterprise root certificate is no longer sufficient.
π¬ How I Diagnosed It
First, I checked whether my local CA file contained a full chain:
grep -c "BEGIN CERTIFICATE" zscaler-root.cer
Result:
1
Only one certificate.
But when I inspected the live TLS connection:
openssl s_client -showcerts -connect registry.npmjs.org:443 \
-servername registry.npmjs.org < /dev/null > /tmp/npm-chain.txt
Then:
grep -c "BEGIN CERTIFICATE" /tmp/npm-chain.txt
Result:
5
Five certificates were involved in the trust chain.
That was the smoking gun.
π The Real Fix: Use the Full Certificate Chain
Step 1 β Split certificates
awk '/BEGIN CERTIFICATE/{i++} {print > ("/c/certs/npm-chain-" i ".pem")}' /tmp/npm-chain.txt
Step 2 β Combine into a full bundle
cat /c/certs/npm-chain-1.pem \
/c/certs/npm-chain-2.pem \
/c/certs/npm-chain-3.pem \
/c/certs/npm-chain-4.pem \
/c/certs/npm-chain-5.pem \
> /c/certs/npm-full-chain.pem
Verify:
grep -c "BEGIN CERTIFICATE" /c/certs/npm-full-chain.pem
Step 3 β Configure npm and Node
npm config set cafile "C:\\certs\\npm-full-chain.pem"
export NODE_EXTRA_CA_CERTS=/c/certs/npm-full-chain.pem
npm cache clean --force
Then:
npm install
Success.
π§ Why This Works
Node validates certificate chains strictly.
When behind corporate SSL inspection:
- The leaf certificate is re-issued by Zscaler.
- Multiple internal CA layers are involved.
- Node must see the entire chain to validate trust.
If any intermediate is missing:
SELF_SIGNED_CERT_IN_CHAIN
Providing only the root CA is not enough in multi-level PKI environments.
π‘ Why Browsers Worked
Browsers use the Windows certificate store.
Node does not (by default).
So:
- Windows trusts enterprise root CA
- Browser succeeds
- Node fails
Different trust stores = different behavior.
π’ Lessons for Enterprise Environments
If you're behind:
- Zscaler
- Blue Coat
- Palo Alto SSL Inspection
- Any corporate TLS interception
And using:
- Node 20+
- Node 22+
- npm 10+
You may need to provide a full certificate bundle, not just a root certificate.
π₯ Key Takeaway
The error wasn't really about npm.
It was about:
Enterprise TLS inspection + multi-level internal PKI + stricter Node.js TLS validation.
Once you understand the trust chain, the fix becomes straightforward.
π Final Thought
If you see:
SELF_SIGNED_CERT_IN_CHAIN
In a corporate environment, donβt disable SSL verification.
Instead:
- Inspect the live TLS chain
- Extract all certificates
- Provide a complete CA bundle to Node
Security preserved. Problem solved.
If you found this helpful, let me know.
Enterprise TLS debugging is painful β but very educational.
Top comments (0)