The DEPTH_ZERO_SELF_SIGNED_CERT error occurs when npm reaches a self-signed certificate at the root of the chain (depth zero). Fix it by trusting that certificate via npm's cafile or your OS trust store.
This error indicates that the very first certificate npm encountered (at "depth zero" in the certificate chain) is self-signed. Unlike SELF_SIGNED_CERT_IN_CHAIN, where a self-signed certificate appears somewhere along an otherwise normal chain, this error means there is effectively no chain of trust at all - just a single self-signed certificate presented by the server. It commonly occurs when talking to development servers, local registries, or proxies that present certificates generated with tools like openssl or mkcert without those certificates having been added to a trusted CA store. Node.js/npm validates the certificate against the system (or configured) trust anchors, finds no matching trusted root, and aborts the TLS handshake.
Inspect the certificate the registry presents:
openssl s_client -connect your-registry:443 </dev/null 2>/dev/null \
| openssl x509 -noout -issuer -subjectIf the issuer equals the subject, the certificate is self-signed (depth zero), which confirms this error.
Save the certificate so you can add it to a trust store:
openssl s_client -connect your-registry:443 </dev/null 2>/dev/null \
| openssl x509 > self-signed.crtIf the registry is reachable in a browser, you can also export the certificate from the browser's certificate viewer as a PEM (.crt/.pem) file.
Point npm at a CA file containing the certificate. cafile is a global npm setting - npm does not support a per-registry cafile key, so configure it once:
npm config set cafile /path/to/self-signed.crtcafile should contain the trust anchor: for a true self-signed leaf, that is the certificate itself; if the server is fronted by a private CA, use that CA's certificate. To combine your private CA with the public roots Node ships, set the environment variable instead so the default roots are kept:
export NODE_EXTRA_CA_CERTS=/path/to/self-signed.crtThis is the safest fix because TLS verification stays enabled - you are only adding a new trust anchor, not disabling checks.
Trusting the certificate at the OS level fixes npm and every other TLS client (git, curl, etc.) at once:
# Ubuntu/Debian
sudo cp self-signed.crt /usr/local/share/ca-certificates/your-registry.crt
sudo update-ca-certificates
# RHEL/Fedora
sudo cp self-signed.crt /etc/pki/ca-trust/source/anchors/your-registry.crt
sudo update-ca-trust
# macOS
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain self-signed.crt
# Windows (run PowerShell/cmd as admin)
certutil -addstore -f "ROOT" self-signed.crtOn Linux, set NODE_EXTRA_CA_CERTS (see step 3) or npm config set cafile as well, because Node does not automatically read the system store.
mkcert installs a local CA into your system (and browser) trust stores, so certificates it issues are trusted automatically - avoiding self-signed errors entirely:
# Install mkcert (examples)
brew install mkcert # macOS
# choco install mkcert # Windows
# apt install mkcert # Debian/Ubuntu (or download the release binary)
# Install the local CA into the trust stores
mkcert -install
# Generate a certificate for your local registry host
mkcert localhost 127.0.0.1 ::1Serve your local registry with the generated *.pem files and npm will trust them with no further configuration.
Prefer the trust-based fixes above. Only if you cannot obtain or install the certificate (for example, a throwaway local test) should you temporarily turn off verification. This disables TLS certificate checks entirely and exposes you to man-in-the-middle attacks, so never do this for production, shared, or CI machines, and re-enable it immediately afterward.
# Per-command (preferred - scoped, not persisted)
npm install --strict-ssl=false
# Or globally (remember to undo it)
npm config set strict-ssl false
# ...then re-enable:
npm config set strict-ssl trueFor production private registries, use certificates issued by a real CA (Let's Encrypt is free and automatable) rather than self-signed certificates, so no client-side trust configuration is needed. In Kubernetes, manage certificates with cert-manager. For local development, mkcert creates locally-trusted certificates that work across tools without per-client setup.
Note the distinction between npm's trust settings: cafile/ca and strict-ssl are global npm config keys, not per-registry keys - only authentication settings (such as //registry:port/:_authToken or :username) are scoped per registry. To make Node itself trust a private CA without replacing its built-in roots, prefer the NODE_EXTRA_CA_CERTS environment variable, which is additive. When distributing to a team, ship the CA via configuration management or document the OS trust-store install steps so every machine trusts it consistently.
npm notice access token expired or revoked. Please try logging in again.
Token has expired - npm authentication failure
npm ERR! code EAI_AGAIN
How to fix "EAI_AGAIN" in npm
npm error code E403 npm error 403 Forbidden - PUT https://registry.npmjs.org/<package>
How to fix 'E403 Forbidden' error in npm
npm ERR! code EUSAGE npm ERR! Usage error
How to fix "npm ERR! code EUSAGE" in Node.js projects
npm ERR! code E401 npm ERR! 401 Unauthorized
How to fix "E401 Unauthorized" in npm