Skip to content
RU

Self-Signed Certificate in Certificate Chain (npm, Node.js, curl, Python): Fix

What it means: The chain ends in a root certificate that your client's trust store does not contain.

  1. Print the chain and read the root
  2. Corporate proxy or antivirus root? Trust it
  3. Private CA? Add its root to clients
  4. Never switch off verification globally

Test your URL now

Runs a full TLS check: handshake, protocol versions, certificate chain, trust and expiry.

Check your site's SSL →

What self-signed certificate in certificate chain means

It is OpenSSL verification error 19, X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: «The certificate chain could be built up using the untrusted certificates but no suitable trust anchor (which typically is a self-signed root certificate) could be found in the trust store» (X509_STORE_CTX_get_error). The server sent a complete chain all the way up to a self-signed root, and that root is not one the client trusts.

The spelling depends on the OpenSSL version: OpenSSL 3 prints self-signed certificate in certificate chain, OpenSSL 1.1.1 printed self signed certificate in certificate chain (x509_txt.c). You will see it as:

  • npm and Node.js: code SELF_SIGNED_CERT_IN_CHAIN, message self-signed certificate in certificate chain.
  • curl: curl: (60) SSL certificate problem: self-signed certificate in certificate chain.
  • Python: certificate verify failed: self-signed certificate in certificate chain; see the Python page for its trust store specifics.

Its neighbour is error 18, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (DEPTH_ZERO_SELF_SIGNED_CERT in Node.js): there the server's own certificate is self-signed, with no chain at all. OpenSSL picks 18 when the self-signed certificate is the only one, and 19 when it sits at the top of a longer chain (x509_vfy.c).

Where the unknown root comes from

Root at the top of the chainWhat is going onRight fix
Your company, a firewall vendor or an antivirus productTLS inspection: a proxy decrypts traffic and re-signs it with its own CAInstall that CA in each tool's trust store
An internal CA (Kubernetes, Vault, an internal PKI)A private service with a private rootDistribute the root to the clients that call it
A public CA root that your client lacksOutdated trust store, old OS or container imageUpdate ca-certificates or the runtime
A root you have never heard of on a public sitePossible interception on the networkStop and investigate before trusting anything

TLS inspection is the most common case on work laptops. The browser trusts the proxy's root because IT installed it in the operating system, but Node.js ships its own list of roots and many Python installs use certifi, so command-line tools still fail. Whether you see error 19 or error 20 depends on the proxy: if it sends its root inside the chain you get 19; if it sends only the re-signed leaf and an intermediate, you get unable to get local issuer certificate.

Identify the root before you trust it

openssl s_client -connect registry.npmjs.org:443 -servername registry.npmjs.org -showcerts </dev/null 2>/dev/null | grep -E '^ *[0-9] s:|^ *i:'

The last certificate whose subject (s:) equals its issuer (i:) is the self-signed root. Run the same command from a network without inspection (a phone hotspot): if the issuer changes, a proxy is re-signing traffic. Before you add any root to a trust store, save it and compare its fingerprint with the one your IT or PKI team publishes:

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null \
  | awk '/BEGIN CERT/{n++; p=1} p{print > ("chain-" n ".pem")} /END CERT/{p=0}'
ls chain-*.pem
openssl x509 -in chain-3.pem -noout -subject -fingerprint -sha256   # the last file is the top of the chain

Trusting a root means trusting everything it signs, for every host. Do it only for a root you can attribute to someone responsible for it.

How to fix it

  1. Operating system (Debian/Ubuntu): cp corp-root.pem /usr/local/share/ca-certificates/corp-root.crt && update-ca-certificates. curl, Git and most system OpenSSL users pick it up.
  2. Node.js: export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-root.crt before starting the process; the variable is read only at launch (Node.js CLI). On Node 23.8+, --use-system-ca makes Node trust the OS store where IT already installed the root.
  3. npm: npm config set cafile /usr/local/share/ca-certificates/corp-root.crt. Leave strict-ssl at its default true.
  4. pip: pip install --cert /path/corp-bundle.pem ... or set the cert option in pip's config; the bundle must contain the corporate root.
  5. Your own service with a private CA: serve leaf + intermediate (the root itself is optional in the chain) and distribute the root to clients through configuration management, not by disabling checks.
  6. Avoid npm config set strict-ssl false, NODE_TLS_REJECT_UNAUTHORIZED=0 and curl -k. They hide this error by accepting any certificate, including an attacker's.

How to confirm the fix

curl -sS -o /dev/null -w '%{http_code}\n' https://registry.npmjs.org/
# 200
node -e "fetch('https://registry.npmjs.org/').then(r => console.log(r.status), e => console.log(e.cause?.code))"
# 200
npm ping

If you run the server, check what the public sees: the SSL/TLS checker builds the chain from outside your network, without any corporate root, and shows whether it ends at a publicly trusted CA.

Related errors

CertificateExpiry, issuer, domains (SAN)
ChainIntermediate and root CA validation
TLS ProtocolTLS version and cipher suite
VulnerabilitiesHeartbleed, POODLE, weak ciphers

Why teams trust us

TLS 1.3
supported
Full
CA chain check
3,158
checks in 30 days
30/14/7
days-to-expiry alerts

How it works

1

Enter domain

2

TLS chain verified

3

Expiry date & vulnerabilities

What Does the SSL Check Cover?

SSL/TLS is the encryption protocol that protects data between the browser and server. Our tool analyzes the certificate, chain of trust, TLS version, and knownvulnerabilities.

Certificate Details

Issuer, validity period, signature algorithm, covered domains (SAN), and validation type (DV/OV/EV).

Chain of Trust

Full chain verification: from leaf certificate through intermediates to root CA.

TLS Analysis

Protocol version (TLS 1.2/1.3), cipher suites, Perfect Forward Secrecy (PFS) support.

Expiry Alerts

Set up a monitor — get Telegram and email alerts 30/14/7 days before expiration.

DV vs OV vs EV Certificates

DV (Domain Validation)
  • Confirms domain ownership only
  • Issued in minutes automatically
  • Free via Let's Encrypt
  • Suitable for most websites
  • Most common certificate type
OV / EV
  • Organization (OV) or Extended Validation (EV)
  • Issued in 1-5 business days
  • Costs $50 to $500/year
  • For finance, e-commerce, government sites
  • Increases user trust

Who uses this

DevOps

SSL certificate monitoring

Security

TLS config audit

SEO

HTTPS as ranking factor

E-commerce

customer trust

Common Mistakes

Expired certificateBrowsers block sites with expired SSL. Set up auto-renewal or monitoring.
Incomplete certificate chainWithout intermediate CA, some browsers and bots cannot verify the certificate.
Mixed content on HTTPS siteHTTP resources on an HTTPS page — the browser lock icon disappears, reducing trust.
Using TLS 1.0/1.1Legacy TLS versions have known vulnerabilities. Use TLS 1.2+ or 1.3.
Domain mismatch in certificateThe certificate must cover all site domains, including www and subdomains.

Best Practices

Set up auto-renewalLet's Encrypt + certbot with cron — certificate renews automatically every 60-90 days.
Enable HSTSStrict-Transport-Security header forces browsers to always use HTTPS.
Use TLS 1.3TLS 1.3 is faster (1-RTT handshake) and safer — legacy ciphers removed.
Monitor expiration datesCreate a monitor on Enterno.io — get notified well before expiration.
Verify chain after renewalAfter certificate renewal, confirm that intermediate certificates are installed.

Get more with a free account

SSL certificate monitoring, check history and alerts 30 days before expiry.

Sign up free

Learn more

Sources

Frequently Asked Questions

Why does npm say SELF_SIGNED_CERT_IN_CHAIN at work but not at home?

Your work network inspects TLS and re-signs traffic with a corporate root. The browser trusts it through the OS, but npm and Node.js use their own root list; add the corporate root with npm config set cafile or NODE_EXTRA_CA_CERTS.

Is it safe to set strict-ssl false in npm?

No. It makes npm accept any certificate for the registry, so a tampered package could be served without a warning. Trust the specific root instead.

What is the difference between DEPTH_ZERO_SELF_SIGNED_CERT and SELF_SIGNED_CERT_IN_CHAIN?

DEPTH_ZERO_SELF_SIGNED_CERT means the server certificate itself is self-signed. SELF_SIGNED_CERT_IN_CHAIN means a longer chain ends in a self-signed root that you do not trust.

Should the server send the root certificate?

It is not needed: clients use the root from their own trust store. Sending it does no harm, but it does not make an untrusted root trusted.

Why is self-signed spelled differently in different error messages?

OpenSSL 3 prints self-signed with a hyphen, OpenSSL 1.1.1 printed self signed without one. Both are error 19.

Try the live tool that powered this guide

Free plan — 10 monitors, checks every 5 min, no card required. Upgrade for 1-minute interval and multi-region monitoring.