# Claude Code SSL Certificate Verification Failed: Fix It by Error Code

> API Error: Unable to connect to API: SSL certificate verification failed (UNABLE_TO_GET_ISSUER_CERT_LOCALLY) or Self-signed certificate detected (SELF_SIGNED_CERT_IN_CHAIN) behind Zscaler or any TLS-inspecting proxy. What the code in brackets means in the 2.1.316 wording, why the OS trust store comes before NODE_EXTRA_CA_CERTS, and the certificate fixes from 2.1.310 to 2.1.318.

By Tahir Nazir · Published 2026-09-06 · Updated 2026-11-04
Canonical: https://www.its-tahir.com/blog/claude-code-ssl-certificate-corporate-proxy

**If Claude Code fails with `API Error: Unable to connect to API: SSL certificate verification failed (UNABLE_TO_GET_ISSUER_CERT_LOCALLY)` or `Self-signed certificate detected (SELF_SIGNED_CERT_IN_CHAIN)`, something between you and Anthropic is presenting a certificate Claude Code doesn't trust, or a server is leaving out an intermediate certificate. Usually it's a TLS-inspecting proxy such as Zscaler or Netskope, or a gateway signed by a private CA. Fix it in this order: get the corporate root into your OS trust store, make sure your runtime can read that store, and only then set `NODE_EXTRA_CA_CERTS`.**

1. **Get your corporate root into the OS trust store** (IT has often done this already).
2. **Make sure your runtime can read that store.** The native installer always can; an npm install needs Node 22.15 or later.
3. **Only if it can't**, set `NODE_EXTRA_CA_CERTS` to the root's `.pem` — and confirm it loaded in `claude --debug`, not `/status`.

If the code in brackets is `CERT_NOT_YET_VALID` or `ERR_TLS_CERT_ALTNAME_INVALID` instead, no certificate setting will help — [read the code first](#if-the-error-names-a-code-read-the-code-first).

If it says `UNABLE_TO_GET_ISSUER_CERT` (no `_LOCALLY`), the problem is usually on your side of the chain: [a leftover CA certificate from a renewal](#is-the-21304-unable_to_get_issuer_cert-bug-fixed). The 2.1.304 bug that produced it is fixed in 2.1.310.

*Last reviewed 4 November 2026, against Claude Code 2.1.318. The error messages are quoted from the 2.1.318 Linux build, which I downloaded from npm into a throwaway folder and searched without running it; the older wording is from Anthropic's error reference.*

Why that order? Most guides repeat only the first half of the message — set `NODE_EXTRA_CA_CERTS`. That works for some people and not others, and the inconsistency is the actual clue. **Claude Code already trusts your operating system's certificate store by default.** From the enterprise network docs:

> Enterprise TLS-inspection proxies work without additional configuration when their root certificate is installed in the OS trust store and the runtime can read it.

So if your corporate CA certificate is already in the OS store and the SSL error persists, adding a `.pem` path is treating a symptom. Start with that second clause instead — *and the runtime can read it*.

![Decision flow for Claude Code SSL errors: CERT_NOT_YET_VALID, CERT_HAS_EXPIRED and ERR_TLS_CERT_ALTNAME_INVALID are clock or proxy-rule problems no CA bundle fixes; UNABLE_TO_GET_ISSUER_CERT_LOCALLY and SELF_SIGNED_CERT_IN_CHAIN need the corporate root in the OS trust store and a runtime that can read it (native installer, or Node 22.15+ on npm), otherwise NODE_EXTRA_CA_CERTS confirmed in claude --debug. Since 2.1.316 the _LOCALLY message reads: The certificates the server sent don't lead to a trusted authority.](https://www.its-tahir.com/blog/claude-code-ssl-certificate-corporate-proxy/ssl-error-decision-flow.svg)

*Read the code in brackets first. Only the two untrusted-issuer codes are fixed by trusting a certificate, and the OS trust store comes before NODE_EXTRA_CA_CERTS.*

## What does the Claude Code SSL error say in each version?

Claude Code 2.1.316 reworded the request-time messages so each code names its own likely cause. In the 2.1.318 build they read:

```text
API Error: Unable to connect to API: SSL certificate verification failed (UNABLE_TO_GET_ISSUER_CERT_LOCALLY). The certificates the server sent don't lead to a trusted authority. If the server leaves out an intermediate certificate, which browsers fetch by themselves, whoever runs it must have it send its full chain. If it is a TLS-inspecting corporate proxy or a gateway signed by a private CA, set NODE_EXTRA_CA_CERTS to that CA bundle, or add it to the system certificate store · see https://code.claude.com/docs/en/network-config

API Error: Unable to connect to API: Self-signed certificate detected (SELF_SIGNED_CERT_IN_CHAIN). The certificate comes from an authority Claude Code doesn't trust, usually a TLS-inspecting corporate proxy or a gateway signed by a private CA: set NODE_EXTRA_CA_CERTS to that CA bundle, or add it to the system certificate store · see https://code.claude.com/docs/en/network-config
```

`UNABLE_TO_VERIFY_LEAF_SIGNATURE` gets the same message as `UNABLE_TO_GET_ISSUER_CERT_LOCALLY`, and `DEPTH_ZERO_SELF_SIGNED_CERT` the same as `SELF_SIGNED_CERT_IN_CHAIN`. In a Claude Desktop session the advice at the end changes to asking your IT team to add the CA to the computer's certificate store. The codes no CA fixes get short messages: `SSL certificate has expired`, `SSL certificate is not yet valid`, `SSL certificate hostname mismatch` and `SSL certificate has been revoked`.

From 2.1.273 to 2.1.315, both trust errors used one sentence, which is still what Anthropic's error reference shows and what most people search for:

```text
API Error: Unable to connect to API: SSL certificate verification failed (UNABLE_TO_GET_ISSUER_CERT_LOCALLY). The certificate comes from an authority Claude Code doesn't trust, usually a TLS-inspecting corporate proxy or a gateway signed by a private CA: set NODE_EXTRA_CA_CERTS to that CA bundle, or add it to the system certificate store · see https://code.claude.com/docs/en/network-config
```

During `/login` or the startup connectivity check, the same failure has a different prefix:

```text
OAuth error: SSL certificate error (CERT_NOT_YET_VALID). If you are behind a corporate proxy or TLS-intercepting firewall, set NODE_EXTRA_CA_CERTS to your CA bundle path, or ask IT to allowlist *.anthropic.com. Run `claude doctor` for details.

OAuth error: SSL certificate error (ERR_TLS_CERT_ALTNAME_INVALID). If you are behind a corporate proxy or TLS-intercepting firewall, set NODE_EXTRA_CA_CERTS to your CA bundle path, or ask IT to allowlist *.anthropic.com. Run `claude doctor` for details.
```

`API Error: Unable to connect to API` appears on requests; `OAuth error` appears at login. Same codes, same fixes. If login fails for a reason that isn't a certificate, such as `Not logged in · Please run /login` or `Login expired`, that's [an authentication problem, not a network one](/blog/claude-code-not-logged-in-login-expired).

Two older version notes from the error reference. Before **v2.1.273**, the request-time messages ended at `Check your proxy or corporate SSL certificates`, with no code and no `NODE_EXTRA_CA_CERTS` hint. And since **v2.1.199**, a certificate failure is reported on the first attempt instead of after the full retry budget; if yours took minutes to appear, you're on an older version.

## If the error names a code, read the code first

Both forms suggest the same fix whatever the code says. The code is what tells you whether it can work. Node's error reference defines them:

| Code in the message | What Node says it means | Will adding a CA fix it? |
| --- | --- | --- |
| `UNABLE_TO_GET_ISSUER_CERT_LOCALLY` | "The certificate's issuer is not known" | **Possibly** — the rest of this post |
| `SELF_SIGNED_CERT_IN_CHAIN` | An untrusted self-signed root sits in the chain | **Possibly** — same family |
| `UNABLE_TO_GET_ISSUER_CERT` | OpenSSL: "the list of trusted certificates is not complete" | **Possibly**: usually a CA certificate on your machine whose issuer is missing after a renewal. [Remove it or add its issuer](#is-the-21304-unable_to_get_issuer_cert-bug-fixed) |
| `UNABLE_TO_VERIFY_LEAF_SIGNATURE` | "the chain contains only one certificate and it is not self signed" | **Usually not**: the chain is incomplete, typically a missing intermediate. Since 2.1.316 the message says so. Fix the chain the proxy or gateway sends |
| `DEPTH_ZERO_SELF_SIGNED_CERT` | "The passed certificate is self-signed" and isn't in the trusted list | **Rarely the right fix** — the server itself presents a self-signed certificate; ask whoever runs that endpoint |
| `CERT_HAS_EXPIRED` | "the notAfter date is before the current time" | **No** — check your clock, then whether the certificate presented to you has actually expired |
| `ERR_TLS_CERT_ALTNAME_INVALID` | "the host name/IP of the peer did not match any of the subjectAltNames in its certificate" | **No** |
| `CERT_NOT_YET_VALID` | "the notBefore date is after the current time" | **No** |

**`ERR_TLS_CERT_ALTNAME_INVALID` is not a trust problem.** The certificate may be perfectly trusted — it's just for a different name. Behind a corporate network that commonly means something in the path answered on Anthropic's behalf with its own certificate: a block page, a captive login, or an interception rule that doesn't cover the API host properly. No CA bundle changes the name on a certificate. The fix sits with whoever runs the proxy: exempt the Anthropic hosts from inspection, or fix the rule.

**`CERT_NOT_YET_VALID` is almost always your clock.** The certificate's start date is in the future *relative to your machine*. VMs resumed from suspend, dual-boot systems and disabled time sync are the usual culprits. Sync the clock before touching any certificate setting.

Short explainers for each code are on the tools site: [UNABLE_TO_GET_ISSUER_CERT_LOCALLY](https://tools.its-tahir.com/errors/unable-to-get-issuer-cert-locally), [SELF_SIGNED_CERT_IN_CHAIN](https://tools.its-tahir.com/errors/self-signed-cert-in-chain), [CERT_NOT_YET_VALID](https://tools.its-tahir.com/errors/cert-not-yet-valid) and [ERR_TLS_CERT_ALTNAME_INVALID](https://tools.its-tahir.com/errors/err-tls-cert-altname-invalid).

Whatever the code, don't reach for `NODE_TLS_REJECT_UNAUTHORIZED=0`. The error reference calls it out by name: it disables certificate validation entirely.

The two "possibly" codes are where the confusion lives, and where the rest of this post applies.

### What does "unable to get local issuer certificate" mean in Claude Code?

It is the plain-text form of `UNABLE_TO_GET_ISSUER_CERT_LOCALLY`: Node and OpenSSL print the code as `unable to get local issuer certificate`, which is the text you see from MCP servers, Bedrock calls and other Node tools, while the API error shows `SSL certificate verification failed (UNABLE_TO_GET_ISSUER_CERT_LOCALLY)`. The fix is to trust that root: install it in the OS trust store, confirm your runtime can read it (native installer, or Node 22.15+ on npm), and only then set `NODE_EXTRA_CA_CERTS`.

OpenSSL's definition is that the issuer certificate of an untrusted certificate cannot be found. In plain terms: Claude Code followed the chain it was given up to an issuer it has no copy of — not in the bundled Mozilla set, not in the OS store it can read, not in `NODE_EXTRA_CA_CERTS`. Behind TLS inspection, that issuer is your proxy's root. The same applies to an LLM gateway behind `ANTHROPIC_BASE_URL` whose certificate comes from a private CA — the message names that case too.

### What does "Self-signed certificate detected (SELF_SIGNED_CERT_IN_CHAIN)" mean?

Here the chain could be built, but it ends in a self-signed root that isn't in any trust store Claude Code uses. Node's error reference gives this code the same one-line description as the one above, and the fix is the same: get that root trusted. The difference is only that the self-signed root arrived as part of the chain the server sent. "Self-signed" here describes that root — root certificates sign themselves — not a misconfigured server. A server presenting a self-signed certificate of its own is a different code, `DEPTH_ZERO_SELF_SIGNED_CERT`.

## Is the 2.1.304 UNABLE_TO_GET_ISSUER_CERT bug fixed?

**Yes.** [Issue #103680](https://github.com/anthropics/claude-code/issues/103680) reported every Bedrock request failing on 2.1.304 behind an HTTPS gateway signed by a private CA, even though `claude --debug` showed the CA certificates loading. Users confirmed the fix in 2.1.310, and the issue was closed on 27 October 2026.

If you followed the earlier workaround and pinned 2.1.303, undo it: remove `"DISABLE_AUTOUPDATER": "1"` from the `env` block of `~/.claude/settings.json`, then move back to the current release ([other install methods](https://code.claude.com/docs/en/setup#install-a-specific-version)):

```bash
claude install latest
```

`UNABLE_TO_GET_ISSUER_CERT` (without `_LOCALLY`) can still appear, for a different reason. Since 2.1.316 it has its own message, which points at the certificates on your machine rather than the proxy:

```text
API Error: Unable to connect to API: SSL certificate verification failed (UNABLE_TO_GET_ISSUER_CERT). The certificates this computer trusts include a CA certificate whose own issuer is missing, often a leftover from a renewal: remove it from the system certificate store or the NODE_EXTRA_CA_CERTS file, or add its issuer there.
```

Root renewals were behind several recent fixes. 2.1.314 fixed failures when the system store holds a renewed root cross-signed with its predecessor, and 2.1.317 fixed `unable to get local issuer certificate` behind some TLS-inspecting proxies after a root certificate renewal. If your IT team renewed the proxy's root recently, update to 2.1.318 before changing anything else. For a private CA whose cross-certificates certify each other in a loop, 2.1.316 added `CLAUDE_CODE_DISABLE_LOOPED_CROSS_CERT_DROP` to keep them.

## Which certificate is my proxy presenting?

Before exporting anything, look at what you actually receive when you connect to Anthropic's API from the affected machine:

```bash
openssl s_client -connect api.anthropic.com:443 -servername api.anthropic.com -showcerts </dev/null
```

If your network only allows outbound traffic through an explicit proxy, add `-proxy proxy.example.com:8080` and `s_client` tunnels through it with an HTTP `CONNECT`.

Read the `Certificate chain` block near the top. Each certificate has an `s:` line (subject) and an `i:` line (issuer). Behind TLS inspection, the issuers name your security vendor or your company's internal CA rather than a public certificate authority — that's the root you need trusted. Two caveats from the OpenSSL manual: `-showcerts` prints only what the server sent, in the order it sent it, and it is **not** a verified chain. And the `Verify return code` line at the end is OpenSSL's verdict against *its own* default trust store, which isn't necessarily the store Claude Code reads.

Once you have the root as a `.pem`, test it before pointing Claude Code at it:

```bash
openssl s_client -connect api.anthropic.com:443 -servername api.anthropic.com -CAfile /path/to/corp-ca.pem </dev/null
```

`Verify return code: 0 (ok)` means that file is the right root. Anything else, and setting `NODE_EXTRA_CA_CERTS` to it won't help either.

If you use Amazon Bedrock or Google Cloud's Agent Platform, model traffic goes to your provider rather than `api.anthropic.com`, so test against the endpoint your requests actually reach.

## Why does NODE_EXTRA_CA_CERTS work for some people and not others?

Reading the OS store requires a runtime that exposes `tls.getCACertificates`:

| Install method | Can read the OS trust store |
| --- | --- |
| Native installer | **Always** |
| npm install | Only on **Node 22.15 or later** |

On an older Node, only the bundled Mozilla set and `NODE_EXTRA_CA_CERTS` apply. That single line explains why the same fix works for your colleague and not for you: they're on the native installer, you're on an npm install with Node 20.

**Check your runtime before changing any certificate setting:**

```bash
node --version        # npm installs: needs >= 22.15 to read the OS store
claude doctor
```

If you're on an npm install with an older Node, you have two real options — upgrade Node, or keep using `NODE_EXTRA_CA_CERTS`, which genuinely is the right tool in that situation.

## What does `CLAUDE_CODE_CERT_STORE` control?

This one is barely mentioned anywhere, and it's the switch that controls the whole behaviour. It takes a comma-separated list of sources:

| Value | Means |
| --- | --- |
| `bundled` | The Mozilla CA set shipped with Claude Code |
| `system` | Your operating system trust store |

**The default is `bundled,system`** — both. To trust only one:

```bash
export CLAUDE_CODE_CERT_STORE=system     # OS store only
export CLAUDE_CODE_CERT_STORE=bundled    # bundled Mozilla set only
```

⚠️ **It has no dedicated `settings.json` schema key.** Set it in the `env` block of `~/.claude/settings.json` or directly in the process environment. Putting it at the top level of a settings file does nothing, silently.

If your corporate CA is in the OS store and the SSL failure persists, check that nothing has narrowed this to `bundled` — that alone will reject every corporate certificate your proxy presents.

## How do I confirm my CA certificate actually loaded?

Run `claude --debug` and read the debug log; `/status` will mislead you.

This is the trap that turns a ten-minute certificate fix into an afternoon.

`/status` shows an **Additional CA cert(s)** row with your `NODE_EXTRA_CA_CERTS` path. It shows the path **without checking the file loaded**. A typo'd path, a permissions problem, a malformed PEM — all look identical to success.

Use the debug log instead:

```bash
claude --debug
```

Output goes to `~/.claude/debug/<session-id>.txt`, not the terminal. Look for these:

```text
CA certs: Appended extra certificates from NODE_EXTRA_CA_CERTS (/etc/ssl/certs/corp-ca.pem)
mTLS: Loaded client certificate from CLAUDE_CODE_CLIENT_CERT
mTLS: Loaded client key from CLAUDE_CODE_CLIENT_KEY
```

If a file couldn't be read, you get a `Failed to read` or `Failed to load` line **with the reason**. That's the difference between guessing and knowing.

A broken file used to take everything down with it. Before 2.1.318, a `NODE_EXTRA_CA_CERTS` file that was empty, wasn't PEM, or started with a damaged certificate made every connection fail. Since 2.1.318 Claude Code uses the certificates it can read and prints a warning on stderr, so check stderr after any change to that file.

The `/status` rows for **mTLS client cert** and **mTLS client key** are more honest — they appear *only* when the files loaded, so a missing row means the load failed.

## Proxy configuration, and the parts that surprise people

```bash
export HTTPS_PROXY=https://proxy.example.com:8080
export NO_PROXY="localhost,192.168.1.1,.example.com"
```

Four things worth knowing:

- **Precedence is fixed.** Claude Code uses the first one set, in the order `https_proxy`, `HTTPS_PROXY`, `http_proxy`, `HTTP_PROXY`. Lowercase variants work. If you set two and get the wrong one, that's why.
- **SOCKS proxies are not supported.** At all.
- **You don't need a loopback entry in `NO_PROXY`.** WebSocket connections to `localhost`, `::1` and `127.0.0.0/8` never go through the proxy.
- **The proxy URL is the one setting validated at startup.** An unparseable value — one missing the `http://` scheme, say — stops launch with an error naming the variable. Everything else on this page fails later, on a request.

And the timing rule underneath all of it: **variables are read once at startup.** A running session doesn't pick up changes to your shell environment, so `export` then retry in the same session proves nothing.

Certificates aren't the only thing an inspecting proxy or gateway can break. One that cuts streaming responses short or rewrites them also breaks auto mode's safety check, which shows up as [auto mode's classifier giving no verdict](/blog/claude-code-auto-mode-classifier-no-verdict) — even on `pwd`.

## Why your settings work in the terminal but not the Desktop Code tab

This one produces a genuinely confusing bug report, and it's deliberate behaviour rather than a fault.

When the Desktop app manages the provider connection, Claude Code reads `NODE_EXTRA_CA_CERTS`, the mTLS variables and `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` **only** from managed settings and `~/.claude/settings.json`. It **ignores them in a repository's own settings files** — so a checked-out repo can't redirect the TLS or proxy path of a session whose credentials come from the app.

That's a sensible security boundary: otherwise cloning a repository could reroute your authenticated traffic. But if you put your CA config in a project's `.claude/settings.json`, it will work in your terminal and silently not in the Desktop Code tab. Related: [issue #22559](https://github.com/anthropics/claude-code/issues/22559).

A local, SSH or WSL Code tab session signed in through claude.ai *isn't* app-managed, so it reads every settings scope like a normal terminal session.

**Cloud sessions ignore these entirely** — the hosting environment owns the connection. `NODE_EXTRA_CA_CERTS`, `NODE_TLS_REJECT_UNAUTHORIZED` and the client-certificate variables are all dropped, and each ignored key is noted in the session's debug log.

## Why don't background agents see my proxy or CA variables?

If you use `claude agents`, `--bg` or `/background`, this will bite you eventually.

Background sessions run under a **per-user supervisor process** that outlives your shell. It inherits the environment of whichever shell started it *first* — and an OS-installed supervisor gets no shell environment at all.

So exporting your proxy or CA variables in `.bashrc` reaches background agents **when that shell happened to cold-start the supervisor, and silently doesn't when a different one did.** Same machine, same config, non-deterministic result.

Put them in the `env` block of `~/.claude/settings.json` instead. Settings are the only configuration that reaches every background session:

```json
{
  "env": {
    "HTTPS_PROXY": "https://proxy.example.com:8080",
    "NODE_EXTRA_CA_CERTS": "/etc/ssl/certs/corp-ca.pem",
    "CLAUDE_CODE_CERT_STORE": "bundled,system"
  }
}
```

An already-running supervisor keeps the configuration it started with, so run `claude daemon stop --any` after changing this.

## Client certificates, if your gateway requires them

```bash
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"
```

Rotation is handled better than most people expect: replace the files at the same paths and a running session picks them up. Since **v2.1.232**, a connection-level failure — a reset or a TLS handshake error — triggers a re-read and a retry with the new pair.

Two caveats. Claude Code re-reads in response to *failures*, not by watching files, so nothing happens at the moment you swap them. And if the gateway completes the handshake and returns an HTTP error instead of resetting, there's no re-read — you wait for the next settings apply or a restart.

## What changed in recent versions?

| Version | Change |
|---|---|
| 2.1.273 | Request-time SSL errors name the code and suggest `NODE_EXTRA_CA_CERTS` |
| 2.1.304 | Regression: `UNABLE_TO_GET_ISSUER_CERT` through private-CA gateways with CAs loaded ([#103680](https://github.com/anthropics/claude-code/issues/103680)) |
| 2.1.310 | That regression fixed, per the users who reported it |
| 2.1.314 | Fixed `UNABLE_TO_GET_ISSUER_CERT` when the system store holds a renewed root cross-signed with its predecessor |
| 2.1.316 | SSL errors reworded to name the likely cause per code, including a missing intermediate; `CLAUDE_CODE_DISABLE_LOOPED_CROSS_CERT_DROP` added |
| 2.1.317 | Fixed `unable to get local issuer certificate` behind some TLS-inspecting proxies after a root certificate renewal |
| 2.1.318 | An empty, non-PEM or damaged `NODE_EXTRA_CA_CERTS` file no longer breaks every connection |

## Triage order

1. **Update to 2.1.318** if the error started after a root certificate renewal or on 2.1.304 to 2.1.309.
2. **Read the code in brackets.** `CERT_NOT_YET_VALID`, `CERT_HAS_EXPIRED` or `ERR_TLS_CERT_ALTNAME_INVALID` mean a clock or proxy-rule problem, not a missing CA.
3. Is your corporate root in the **OS trust store**? `openssl s_client … -showcerts` tells you which root the proxy is presenting. If it's installed, this should already work, so go to step 4 rather than adding a `.pem`.
4. **Can your runtime read the OS store?** Native installer, or npm on Node ≥ 22.15. If not, that's your SSL error explained.
5. Check nothing narrowed **`CLAUDE_CODE_CERT_STORE`** away from `bundled,system`.
6. Set `NODE_EXTRA_CA_CERTS` only if step 4 rules out the OS store. Test the file with `openssl s_client -CAfile` first, then **confirm in the debug log**, not `/status`.
7. If it works in the terminal but not the Desktop Code tab, move the config to `~/.claude/settings.json`.
8. If it works interactively but not for background agents, move it to the `env` block and `claude daemon stop --any`.

**Key takeaways**

- The error's advice is right, in the opposite order: OS trust store first, NODE_EXTRA_CA_CERTS only when your runtime can't read it.
- The 2.1.304 UNABLE_TO_GET_ISSUER_CERT regression is fixed in 2.1.310. If you pinned 2.1.303, remove DISABLE_AUTOUPDATER and run claude install latest.
- Since 2.1.316 the message names the likely cause per code: a missing intermediate for _LOCALLY, a leftover renewed CA for UNABLE_TO_GET_ISSUER_CERT.
- API Error: Unable to connect to API appears on requests; OAuth error: SSL certificate error appears at login. Same codes, same fixes.
- Claude Code trusts your OS certificate store by default — CLAUDE_CODE_CERT_STORE defaults to bundled,system.
- Reading the OS store needs tls.getCACertificates: the native installer always has it, npm installs need Node 22.15+. That is why the same fix works for one person and not another.
- CERT_NOT_YET_VALID and ERR_TLS_CERT_ALTNAME_INVALID are not trust problems — no CA bundle fixes them.
- /status shows the NODE_EXTRA_CA_CERTS path without checking the file loaded. Confirm in claude --debug output instead.
- Background agents run under a supervisor that inherits whichever shell started it first, so shell exports reach them unreliably. Use the env block in settings.

## FAQ

The reason the Claude Code SSL certificate error burns so much time in enterprises isn't that it's hard — it's that the default behaviour is better than people expect, so everyone reaches for the override first and then debugs the override. Establish whether the OS store is being read at all, and most of these tickets close in ten minutes.

## Related errors

| If you're seeing | Read |
| --- | --- |
| The same certificate errors in `npm`, `pip`, `git` or `curl` | [What each tool actually trusts behind a corporate proxy](/blog/corporate-proxy-ssl-npm-pip-git-curl) — tested settings, and which ones break off the network |
| `Failed to fetch` Google Fonts in `next build` on the same corporate network | [Next.js Google Fonts behind a corporate proxy](/blog/nextjs-failed-to-fetch-google-fonts-corporate-proxy) — the same proxy breaks `next/font`, and `NODE_EXTRA_CA_CERTS` behaves differently there |
| `Not logged in · Please run /login` or `Login expired` | [What each Claude Code login message means](/blog/claude-code-not-logged-in-login-expired) |
| `There's an issue with the selected model` through a gateway or `ANTHROPIC_BASE_URL` | [Why "issue with the selected model" is usually a 404](/blog/claude-code-issue-with-the-selected-model) |
| `404` or model not found on Google Cloud's Agent Platform (Vertex AI) | [Claude Code on Vertex AI: 404s and wrong regions](/blog/claude-code-vertex-ai-model-not-found) |
| Auto mode blocking every command behind a gateway or proxy | [Auto mode's classifier gave no verdict](/blog/claude-code-auto-mode-classifier-no-verdict) |
| `529 Overloaded` or failures that look like network problems | [What Claude Code retries on a 529, and what it won't](/blog/claude-code-529-overloaded-retry-behaviour) |
| Claude Code won't start at all on Windows | [Raw mode is not supported, and the other Windows startup errors](/blog/claude-code-raw-mode-is-not-supported-windows) |

For anything not in this table, [the full list of Claude Code errors and fixes](/blog/claude-code-troubleshooting) covers every error I've written up, grouped by cause.

## FAQ

### What does 'The certificates the server sent don't lead to a trusted authority' mean?

It is how Claude Code 2.1.316 and later describe UNABLE_TO_GET_ISSUER_CERT_LOCALLY and UNABLE_TO_VERIFY_LEAF_SIGNATURE. Either the server leaves out an intermediate certificate, which browsers fetch on their own and Claude Code does not, so whoever runs it must send the full chain; or a TLS-inspecting proxy or a gateway signed by a private CA re-signs the traffic, so its root has to be trusted: OS trust store first, NODE_EXTRA_CA_CERTS only if your runtime can't read that store.

### Is the UNABLE_TO_GET_ISSUER_CERT bug from Claude Code 2.1.304 fixed?

Yes. GitHub issue #103680, where requests through a private-CA gateway failed on 2.1.304 even though claude --debug showed the CA certificates loaded, was closed on 27 October 2026 after users confirmed the fix in 2.1.310. If you pinned 2.1.303 and set DISABLE_AUTOUPDATER, remove that setting and run claude install latest. Since 2.1.316, UNABLE_TO_GET_ISSUER_CERT has its own message: a CA certificate your computer trusts has a missing issuer, often a leftover from a renewal, so remove it from the system store or the NODE_EXTRA_CA_CERTS file, or add its issuer.

### What does 'SSL certificate verification failed (UNABLE_TO_GET_ISSUER_CERT_LOCALLY)' mean in Claude Code?

Claude Code could not find the issuer of the certificate it was shown in any trust store it uses. Behind a corporate network this almost always means a TLS-inspecting proxy such as Zscaler or Netskope is re-signing traffic with a company root certificate. Install that root in your operating system trust store and make sure your runtime can read it: the native installer always can, npm installs need Node 22.15 or later. Only if it cannot, point NODE_EXTRA_CA_CERTS at the root and confirm it loaded with claude --debug.

### Why does NODE_EXTRA_CA_CERTS work for some people and not others?

Because it is usually not the thing that matters. Claude Code trusts both its bundled Mozilla CA set and your operating system trust store by default, so if your corporate root is installed in the OS store it should already work. Reading the OS store needs a runtime with tls.getCACertificates: the native installer always has it, but npm installs need Node 22.15 or later. On an older Node only the bundled set and NODE_EXTRA_CA_CERTS apply, which is why the same advice fixes it for one person and not the next.

### What is CLAUDE_CODE_CERT_STORE and what is the default?

It takes a comma-separated list of certificate sources. The recognized values are bundled, meaning the Mozilla CA set shipped with Claude Code, and system, meaning your operating system trust store. The default is bundled,system. It has no dedicated settings.json schema key, so set it in the env block of your settings file or directly in the process environment.

### Why did my certificate settings stop applying in the Claude Desktop Code tab?

When the Desktop app manages the provider connection, Claude Code reads the TLS and proxy variables only from managed settings and ~/.claude/settings.json. It deliberately ignores them in a repository's own settings files, so a checked-out repo cannot redirect the TLS or proxy path of a session whose credentials come from the app.

### How do I confirm my CA certificate actually loaded?

Run claude --debug and read ~/.claude/debug/<session-id>.txt for a line reading 'CA certs: Appended extra certificates from NODE_EXTRA_CA_CERTS'. Do not rely on /status alone; its Additional CA cert(s) row shows the configured path without checking that the file loaded, so it looks identical whether the load succeeded or failed.

## Sources

- [Enterprise network configuration — Claude Code docs](https://code.claude.com/docs/en/network-config)
- [Error reference: SSL certificate errors — Claude Code docs](https://code.claude.com/docs/en/errors)
- [Environment variables reference — Claude Code docs](https://code.claude.com/docs/en/env-vars)
- [Desktop app does not forward NODE_EXTRA_CA_CERTS to the CLI subprocess (issue #22559)](https://github.com/anthropics/claude-code/issues/22559)
- [Bedrock: UNABLE_TO_GET_ISSUER_CERT on 2.1.304, custom CAs loaded but not used (issue #103680, closed 27 October 2026)](https://github.com/anthropics/claude-code/issues/103680)
- [Claude Code changelog (2.1.273 to 2.1.318)](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md)
- [@anthropic-ai/claude-code-linux-x64 2.1.318 (npm)](https://www.npmjs.com/package/@anthropic-ai/claude-code-linux-x64)
- [Set up Claude Code: install a specific version and disable auto-updates — Claude Code docs](https://code.claude.com/docs/en/setup)
- [CLI reference: claude install [version] — Claude Code docs](https://code.claude.com/docs/en/cli-reference)
- [Errors — Node.js documentation (TLS and OpenSSL error codes)](https://nodejs.org/api/errors.html)
- [openssl-s_client — OpenSSL documentation](https://docs.openssl.org/master/man1/openssl-s_client/)
- [X509_STORE_CTX_get_error (certificate verification error codes) — OpenSSL documentation](https://docs.openssl.org/master/man3/X509_STORE_CTX_get_error/)
