Skip to content
Latchkey

Node.js "self signed certificate in certificate chain" - Fix TLS in CI

Node could not validate the TLS chain because it ends in a self-signed certificate it does not trust - almost always a corporate proxy re-signing HTTPS. The fix is to trust the proxy’s CA, not to switch verification off.

What this error means

npm install, a fetch, or any HTTPS call fails with self signed certificate in certificate chain (code SELF_SIGNED_CERT_IN_CHAIN). It is environment-specific - it passes on an unproxied machine and fails behind the corporate proxy.

npm / Node output
npm error code SELF_SIGNED_CERT_IN_CHAIN
npm error errno SELF_SIGNED_CERT_IN_CHAIN
npm error request to https://registry.npmjs.org/... failed,
reason: self signed certificate in certificate chain

Common causes

A TLS-intercepting proxy

A corporate proxy terminates TLS and re-signs traffic with its own root CA. Node does not trust that CA, so the chain validation fails with a self-signed-certificate error.

Missing CA bundle on a minimal image

A slim runner image without an up-to-date CA store cannot validate otherwise-legitimate chains.

How to fix it

Trust the proxy’s CA (correct fix)

Point Node and npm at the extra CA file instead of disabling verification.

Terminal
# Node: add the corporate root CA
export NODE_EXTRA_CA_CERTS=/etc/ssl/certs/corporate-root.pem
# npm: point at the same CA bundle
npm config set cafile /etc/ssl/certs/corporate-root.pem
npm ci

Never disable verification permanently

Setting NODE_TLS_REJECT_UNAUTHORIZED=0 makes Node accept any certificate - it turns off the protection that catches real interception. Use it only to confirm the diagnosis, never as the fix.

Terminal
# diagnosis ONLY - do not leave this set
NODE_TLS_REJECT_UNAUTHORIZED=0 node -e "require('https').get('https://registry.npmjs.org', r=>console.log(r.statusCode))"

How to prevent it

  • Install the proxy’s root CA into the runner image and use NODE_EXTRA_CA_CERTS.
  • Keep ca-certificates current on runner images.
  • Never leave NODE_TLS_REJECT_UNAUTHORIZED=0 set in CI.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →