The error, verbatim
Jump to the fix ↓npm error code SELF_SIGNED_CERT_IN_CHAIN
npm error errno SELF_SIGNED_CERT_IN_CHAIN
npm error request to https://registry.npmjs.org/is-number failed, reason: self-signed certificate in certificate chain
Tested on
- Node / npm
- 24.14.0 / 11.9.0
- OS
- Windows (GitHub windows-latest), Ubuntu, Windows 11 Pro
- Setup
- local HTTPS proxy with a private root CA
✓ Last re-run on GitHub Actions · see the run · all repros
Contents
This error almost never means something is wrong with npm. Something between you and the registry, usually a company proxy or VPN that inspects HTTPS traffic, opens each connection and re-signs it with its own root certificate. Your browser trusts that root because IT installed it in Windows. Node doesn’t use the Windows certificate store; it ships its own list, so it sees a root it has never heard of and refuses.
To reproduce it, I ran a small HTTPS proxy on localhost that forwards to the real npm registry and signs everything with a private root CA, the way an inspecting proxy does. Then I pointed npm and Node at it, on GitHub’s Windows and Ubuntu runners. Both gave the same error:
npm error code SELF_SIGNED_CERT_IN_CHAIN
npm error request to https://localhost:8443/is-number failed, reason: self-signed certificate in certificate chain
fetch() in Node failed the same way: fetch failed, with cause.code SELF_SIGNED_CERT_IN_CHAIN.
Step 1: get the company’s root certificate as a file
If your browser can open the site, the root is already in Windows. Export it as a .pem file. In PowerShell, find it by name (ask IT if you don’t know it), then write it out:
$c = Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -match 'YourCompany' } | Select-Object -First 1
$pem = "-----BEGIN CERTIFICATE-----`n" + [Convert]::ToBase64String($c.RawData, 'InsertLineBreaks') + "`n-----END CERTIFICATE-----"
Set-Content C:\certs\corp-root.pem $pem -Encoding asciiThis only reads a certificate that’s already installed. It doesn’t change what Windows trusts.
Step 2: point npm and Node at it
For npm and everything else that runs on Node (npx, fetch(), build tools), set the variable before starting Node:
$env:NODE_EXTRA_CA_CERTS = "C:\certs\corp-root.pem"For npm only, put it in the project’s .npmrc, or pass it once:
cafile=C:\certs\corp-root.pemTo keep the variable for new terminals, set it as a user environment variable (System Properties → Environment Variables) rather than in one PowerShell window.
Two ways it still fails with the fix in place
What I didn’t use
npm config set strict-ssl false and NODE_TLS_REJECT_UNAUTHORIZED=0 both made the install succeed. They do it by switching off certificate checks entirely, so anything on the network path could serve you a different package. NODE_TLS_REJECT_UNAUTHORIZED=0 was also slow: the same install took about 2 minutes instead of under a second, on both systems. Use the root certificate instead.
How this was tested
Node 24.14.0 and npm 11.9.0 on GitHub Actions’ windows-latest and ubuntu-latest runners, October 15, 2026. A small Node HTTPS proxy on localhost:8443 forwarded to registry.npmjs.org and presented a certificate signed by a private root CA created for the test; each case installed is-number through it in a fresh project. The export and the Avast observation were on a Windows 11 Pro PC. The script and workflow are public. Every message above is the real output, except that the error at the top shows the registry’s address where the test used the proxy’s.
— N.K., end of entry No.068