Claude Code that worked at home stops at the office with a wall of TLS noise: SELF_SIGNED_CERT_IN_CHAIN, unable to get local issuer certificate, or a blunt Self-signed certificate detected.
Nothing is wrong with your install. Your company's network is opening every HTTPS connection, re-signing it, and Claude Code does not recognize the new signature.
This guide covers the five ways to fix that, in the order you should try them, plus the proxy settings that go with them — and the one "fix" that quietly hands your API key to anyone on the wire.
If you'd rather have someone wire this up for a whole team, we do that too.
30-SECOND DIAGNOSIS
Read the error string first
The error string tells you which layer is failing, and each one maps to a specific fix below.
| Error string | What it means | First fix to try |
|---|---|---|
| SELF_SIGNED_CERT_IN_CHAIN | A proxy re-signed traffic with a CA you don't trust. | Fix 1 (OS store) or Fix 2 (NODE_EXTRA_CA_CERTS) |
| unable to get local issuer certificate | The issuing CA isn't in any store Claude Code reads. | Fix 2 (NODE_EXTRA_CA_CERTS) |
| Self-signed certificate detected | Claude Code's own wording for the two above. | Fix 1, then Fix 2 |
| CERT_HAS_EXPIRED | Clock skew, or a stale intercept cert. | Check the system clock, then Fix 1 |
| curl: (35) TLS connect error on install | Handshake fails before install even starts. | Fix 3 (install-time flags) |
| schannel: ... SSL/TLS secure channel (Windows) | Windows trust or TLS version at install. | Fix 3 (TLS 1.2, revocation) |
One test settles the cause in ten seconds. Run Claude Code on a network without inspection — a phone hotspot works — or ask IT for a bypass rule on api.anthropic.com. If it connects there and fails on the corporate network, the proxy is re-signing your traffic and you need a CA fix, not a reinstall.
FIRST DECISION
Fix the trust, or route around it?
Before you touch a config file, decide which problem you actually have. The certificate fixes assume you can get your company's CA and set an environment variable. That is not always true, and forcing it wastes an afternoon.
Add the CA (Fixes 1–5) when
- You control the machine and can set environment variables or edit ~/.claude/settings.json.
- IT can hand you the root CA file, or it is already installed in your OS trust store.
- The proxy uses plain pass-through or basic authentication.
Route through a gateway when
- The proxy demands NTLM or Kerberos authentication, which Claude Code does not speak.
- You cannot obtain the corporate CA and IT will not allowlist Anthropic's domains.
- You need one egress point with its own TLS termination for a whole team, so per-developer CA juggling stops being worth it.
Stop rule. If Claude Code connects on a direct network and only breaks behind the corporate proxy, this is a trust problem, full stop. Do not reinstall, do not switch Node versions blindly, do not open a support ticket about a "broken binary." Skip to Fix 1. If it also fails on a clean network, the cause is something else — DNS, a regional block, an expired subscription — and the certificate fixes here will not help.
WHY CLAUDE CODE REJECTS THE CERT
Your network is re-signing the traffic
Most corporate networks run a TLS-inspection proxy. Knowing what it does tells you why the error appears and where the fix lives.
Zscaler, Netskope, Cato, CrowdStrike Falcon, or a full-tunnel VPN all do the same job: read outbound HTTPS for data-loss prevention and malware scanning. To read an encrypted stream, the box has to sit in the middle. It terminates your connection to api.anthropic.com, decrypts it, then makes its own connection onward and re-signs the response with the company's private certificate authority.
Your browser trusts that re-signed certificate because IT pushed the corporate root CA into the browser and OS trust stores when they set up the laptop. Claude Code is a separate runtime with its own idea of what to trust — and that is where the mismatch lives.
Here is the part most write-ups get wrong. Modern Claude Code already reads your OS trust store. By default it trusts both its bundled Mozilla CA set and the operating system's certificate store. Reading the OS store needs a runtime that exposes tls.getCACertificates: the native installer always has it, and npm installs need Node 22.15 or later. On older Node, only the bundled set and NODE_EXTRA_CA_CERTS apply. So when IT has installed the corporate root in the OS store and you are on a current build, inspection proxies like Zscaler and CrowdStrike Falcon work with no extra configuration at all. The errors show up in the gaps: an old npm-based install, a CA that never made it into the OS store, or a runtime launched in a way that skips it.
READING THE ERROR
Strings and scope
The exact string narrows the cause. This is the same taxonomy Node and OpenSSL use, so it applies whether Claude Code runs on the native binary or an npm install.
| Error | Layer | Root cause | Scope |
|---|---|---|---|
| SELF_SIGNED_CERT_IN_CHAIN | TLS verify | Proxy's root CA absent from the trust store | Every API call |
| unable to get local issuer certificate | TLS verify | Intermediate or root CA missing from the chain | Every API call |
| CERT_HAS_EXPIRED | TLS verify | Wrong system clock, or a stale intercept cert | Every API call |
| schannel: ... secure channel | Windows TLS | Windows trust store or TLS version mismatch at install | Install step |
| CRYPT_E_NO_REVOCATION_CHECK (0x80092012) | Windows revocation | Network blocks the revocation (OCSP/CRL) lookup | Install step |
| CRYPT_E_REVOCATION_OFFLINE (0x80092013) | Windows revocation | Revocation endpoint unreachable behind the firewall | Install step |
The split matters. A verify error on every API call is a trust-store problem you fix once with a CA. A revocation error at install time is the firewall blocking a lookup, and you work around it for the single install command rather than changing your trust configuration.
THE FIXES
The fixes, in order
Work top down. The earlier fixes are cleaner and less risky than the later ones.
Fix 1: Let Claude Code use the OS trust store
If IT already put the corporate CA in your system store — they almost always do, because the browser needs it — the cleanest fix is to make sure Claude Code reads that store rather than fighting it.
On the native installer this is automatic. On an npm install, confirm your Node is 22.15 or newer:
node --version
If it is older, upgrade Node or switch to the native installer, which never depends on the Node version for trust. The default for CLAUDE_CODE_CERT_STORE is already bundled,system, so Claude Code reads the OS store out of the box. You only touch this variable if someone forced it to bundled, which drops the OS store; in that case put system back:
export CLAUDE_CODE_CERT_STORE=bundled,system
Expected result: Claude Code trusts what your OS trusts, including the corporate root. Forcing system alone adds no trust the default did not already have, so it will not rescue a runtime too old to read the OS store. If the OS store itself is missing the CA, that is Fix 2.
Fix 2: Point NODE_EXTRA_CA_CERTS at the corporate CA
This is the workhorse fix, and the one Anthropic's docs name for TLS-inspection environments. It adds your corporate root on top of the built-in set without replacing anything.
First get the CA file in PEM format. Ask IT, or export it yourself: on macOS open Keychain Access and export the corporate root as a .pem; on Windows run certmgr.msc, open Trusted Root Certification Authorities, and export as Base-64 encoded X.509; on Linux the file usually already lives under /usr/local/share/ca-certificates or /etc/pki/ca-trust.
Then point Claude Code at it:
export NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem claude
Expected result: API calls succeed because Claude Code now trusts the proxy's re-signed certificate. Make it permanent by adding the export to ~/.zshrc or ~/.bashrc. If your CA bundle has multiple certs (a root plus intermediates), concatenate them into one PEM file; NODE_EXTRA_CA_CERTS reads them all.
Fix 3: Fix the install step separately
The install and the runtime are two different network calls, and they can fail independently. If curl chokes before Claude Code is even installed, patch the install command, not your shell profile.
Point the installer's curl at the same CA bundle:
curl --cacert /path/to/corporate-ca.pem -fsSL https://claude.ai/install.sh | bash
On Windows, if you see the schannel secure-channel error, force TLS 1.2 first:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 irm https://claude.ai/install.ps1 | iex
If Windows reports CRYPT_E_NO_REVOCATION_CHECK or CRYPT_E_REVOCATION_OFFLINE, the network is blocking the certificate revocation lookup, which is common behind corporate firewalls. Add best-effort revocation to the install only:
curl --ssl-revoke-best-effort -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
Expected result: the binary installs. Runtime API calls are still governed by Fix 1 or Fix 2, so set those too.
Fix 4: Configure the proxy itself
Sometimes the certificate is fine but the proxy is not being used. Claude Code reads the standard proxy variables. HTTPS_PROXY is the one that matters for API traffic:
export HTTPS_PROXY=https://proxy.example.com:8080 export HTTP_PROXY=http://proxy.example.com:8080 export NO_PROXY="localhost,127.0.0.1,.internal.example.com"
NO_PROXY takes a space- or comma-separated list, and * bypasses the proxy for everything. If the proxy needs basic auth, put the credentials in the URL:
export HTTPS_PROXY=http://username:password@proxy.example.com:8080
Two limits worth knowing before you burn time on them: Claude Code does not support SOCKS proxies, and it does not handle NTLM or Kerberos proxy authentication — which is exactly the case the gateway section is about.
Fix 5: mTLS and client-certificate environments
Some enterprises require a client certificate, not just a trusted server one. Claude Code supports mutual TLS through three variables:
export CLAUDE_CODE_CLIENT_CERT=/path/to/client-cert.pem export CLAUDE_CODE_CLIENT_KEY=/path/to/client-key.pem export CLAUDE_CODE_CLIENT_KEY_PASSPHRASE="your-passphrase"
Expected result: Claude Code presents the client certificate during the handshake, satisfying a proxy or gateway that authenticates callers by certificate rather than by header.
The anti-fix: never disable verification
You will find NODE_TLS_REJECT_UNAUTHORIZED=0 recommended on forums. It makes the error disappear because it tells Node to accept any certificate from anyone. That includes an attacker sitting on the same coffee-shop network, who can now read the API key you send on every request in plaintext.
It is the one line you should never commit. Adding the CA keeps validation on and trusts only the certificate you chose; disabling validation trusts everything. Those are not close.
FULL WALKTHROUGH
Zscaler on macOS, end to end
Abstract steps are easy to fumble, so here is the whole thing for the most common setup — a Mac behind Zscaler. It takes about five minutes.
- 1.Confirm it is really the proxy. Tether to your phone and run claude. If it connects there, the corporate network is intercepting and you continue below.
- 2.Export the Zscaler root from the Keychain. Open Keychain Access, search for the corporate root (often named Zscaler Root CA), select it, and use File then Export Items to save it as zscaler-root.pem in your home folder. If it exports as .cer, convert it:
- 3.Point Claude Code at it and make it stick:
openssl x509 -inform der -in zscaler-root.cer -out zscaler-root.pem
echo 'export NODE_EXTRA_CA_CERTS="$HOME/zscaler-root.pem"' >> ~/.zshrc source ~/.zshrc
Open a fresh terminal so the export is loaded, then start claude. The SSL error is gone.
If curl verifies with that bundle but claude still fails, the variable is not reaching Claude Code's process. That is the shell-versus-settings.json gap covered next, not a certificate problem.
curl --cacert "$NODE_EXTRA_CA_CERTS" -svo /dev/null https://api.anthropic.com 2>&1 | grep -E "issuer|verify"
CONTAINERS & CI
Docker, CI runners, and remote dev
Containers are where this error resurfaces after you thought it was solved, because a fresh container does not inherit your laptop's OS trust store. Your host trusts the corporate CA; the Ubuntu base image inside Docker does not.
Bake the CA into the image and register it in both the OS store and the Node variable:
COPY corporate-ca.pem /usr/local/share/ca-certificates/corporate-ca.crt RUN update-ca-certificates ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corporate-ca.crt
The same logic covers CI runners and remote dev boxes: a GitHub Actions or GitLab runner behind the same proxy needs the CA present in the job environment, usually as a secret file written at the start of the job with NODE_EXTRA_CA_CERTS exported before any claude step. Devcontainers need the ENV line above in their Dockerfile. If you only fixed your shell profile, the container never saw it.
WHERE TO SET IT
settings.json vs the shell
Every variable above can live in the env block of ~/.claude/settings.json, which is the tidy option for a machine you configure once.
{
"env": {
"NODE_EXTRA_CA_CERTS": "/path/to/corporate-ca.pem",
"HTTPS_PROXY": "https://proxy.example.com:8080"
}
}There is a caveat worth stating plainly. Some earlier versions ignored NODE_EXTRA_CA_CERTS when it was set only in settings.json, and the desktop app did not always forward it to the CLI subprocess it spawns. Both have surfaced as tracked GitHub issues. If the settings.json value does nothing, the reliable fallback is the shell export in your profile, which every build honors because Node reads it before Claude Code starts.
| Where you set it | Reliability | Use when |
|---|---|---|
| Shell profile (.zshrc/.bashrc) | Highest — every build reads it | Anything, especially if settings.json seems ignored |
| ~/.claude/settings.json env block | High on current builds | You want one committed config per machine |
| Desktop app environment | Lowest — may not reach the CLI subprocess | Only after confirming the CLI actually inherits it |
RELATED ERRORS
Errors that get mistaken for the cert bug
These show up next to the certificate errors and get mistaken for them.
| Symptom | Cause | Fix |
|---|---|---|
| curl: (56) Failure writing output on install | Download interrupted, often by the proxy | Retry, or use brew/winget which avoid piped curl |
| Install returns HTML or 403 | Region unsupported, or a proxy blocking downloads.claude.ai | Confirm your region is supported, then allowlist the domain or set HTTPS_PROXY |
| unable to get local issuer certificate at install | CA missing during the curl step | Add --cacert (Fix 3) |
| MCP server over HTTPS won't trust its cert | The MCP endpoint uses a self-signed cert | Add that cert to NODE_EXTRA_CA_CERTS too |
| Works in terminal, fails in the IDE extension | The IDE process didn't inherit your shell env | Set the vars in the IDE settings, or launch it from a terminal |
That last row is the quiet one. If claude works in your terminal but the VS Code or JetBrains panel throws the SSL error, the editor was launched from the dock and never saw your .zshrc. Set the variables in the IDE's own environment, or start the editor from a shell where they are already exported.
WHEN THE PROXY WON'T BUDGE
Route through a gateway
If the proxy demands NTLM or Kerberos, or you simply cannot get the CA, adding certificates is a dead end. Put an LLM gateway in front that speaks that scheme.
A gateway terminates its own TLS and handles the messy proxy negotiation once, so every developer points at one endpoint instead of each person fighting their own trust store. Pointing Claude Code at a custom endpoint is a one-line change — set two variables:
export ANTHROPIC_BASE_URL=https://your-gateway.example/anthropic export ANTHROPIC_API_KEY=sk-...
Be honest about what this does and does not solve. The corporate proxy still re-signs traffic to the gateway host, so you may still need the corporate CA in your trust store for it. What the gateway buys you is one egress endpoint for the whole team, a single billing and usage view across providers, and a place to put failover when one upstream is unreachable.
The gateway is the escape hatch, not the default. When you can get the CA, Fix 1 or Fix 2 is simpler and keeps you on the direct path.
WHAT THIS ACTUALLY TEACHES
A trust layer quietly turned into a security hole
The fixes above get Claude Code talking through your proxy. The rest is the part worth acting on before someone ships the anti-fix.
You can read this error two ways. The narrow way: a proxy re-signs your traffic, add the CA, move on. The useful way: a security control your company put in place is now the thing blocking your tooling — and the most common workaround people reach for silently removes the protection entirely.
We see this in nearly every AI system we audit. A team slaps NODE_TLS_REJECT_UNAUTHORIZED=0 into a script to make an error go away, that script gets copied into CI, and a year later an attacker on the network is reading every API key it sends. The certificate error is a small thing. The reflex to disable validation is the real vulnerability.
Three things make a setup survive these environments:
Trust explicitly, never globally
Add the specific CA your company issued. Never turn off validation for the whole process — it trades a five-minute fix for a permanent attack surface.
Set trust at the boundary, not the laptop
Bake the CA into the image and the CI runner. If trust lives only on one developer's shell profile, it breaks the day someone else runs the pipeline.
Know what your escape hatch costs
A gateway solves NTLM and team egress. It does not remove the corporate proxy from the path. Count the trust hops before you assume a route is clean.
If your team's workaround for a certificate error is to turn off certificate checks, the error wasn't the problem — the workaround was.
FAQ
Questions we get about this
How do I fix SELF_SIGNED_CERT_IN_CHAIN in Claude Code?+
A TLS-inspection proxy re-signed the connection with a CA you don't trust. Install that CA in the OS trust store (the native installer and Node 22.15+ read it automatically) or point NODE_EXTRA_CA_CERTS at the CA bundle. Do not disable validation.
What is NODE_EXTRA_CA_CERTS and where do I point it?+
A Node variable that adds trusted certificates on top of the built-in set. Point it at your corporate root in PEM format: export NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem. Set it in the shell or in settings.json.
Does Claude Code read the Windows or macOS certificate store?+
Yes, by default it trusts the bundled Mozilla set plus the OS store. Reading the OS store needs the native installer or Node 22.15+; older Node uses only the bundled set and NODE_EXTRA_CA_CERTS.
Is NODE_TLS_REJECT_UNAUTHORIZED=0 safe?+
No. It turns off certificate validation for every request, exposing your API key to anyone on the path. Add the CA instead — it keeps validation on and trusts only the certificate you chose.
Why does Claude Code fail with an SSL error when curl works?+
They read different trust stores. System curl usually already trusts the corporate CA; Claude Code's runtime may not, or an npm install is on Node older than 22.15. Point NODE_EXTRA_CA_CERTS at the same CA, or move to the native installer or Node 22.15+ so it reads the OS store.
Can I set NODE_EXTRA_CA_CERTS in settings.json?+
Yes, in the env block. Some older versions honored only the shell export, so if settings.json does nothing, export it in your shell profile — Node reads it before Claude Code starts.
Does Claude Code support SOCKS or NTLM proxies?+
No SOCKS, and no NTLM or Kerberos auth directly. It reads HTTP_PROXY, HTTPS_PROXY, and NO_PROXY, with basic auth as user:password in the URL. For NTLM or Kerberos, front it with a gateway.
How do I get my company's CA certificate file?+
Ask IT for the root CA in PEM format, or export it from Keychain Access (macOS), certmgr.msc (Windows, Base-64 X.509), or /usr/local/share/ca-certificates (Linux).
About FACTA
FACTA helps startups and growth-stage teams turn AI into production systems that keep running — not demos that impress once.
We design the architecture around the parts that actually break under real usage: model abstraction, credentials you own, failover, cost controls, observability. The boring infrastructure that keeps a system alive after launch.
Led by Matías Baglieri and Carolina Fogliato, we focus on one thing:
AI leadership that builds. Not just advises.
Wire Claude Code into your corporate network — without the security hole
SSL and proxy trust is the kind of tooling friction that quietly turns into risk. Our AI automation team configures Claude Code, gateways, and CI trust for teams behind TLS-inspection proxies — keeping validation on and API keys off the wire.
Explore AI Automation