WebFetch tool fails with SSL error behind Zscaler/corporate proxy (Bun runtime ignores NODE_EXTRA_CA_CERTS)

Resolved 💬 3 comments Opened Feb 15, 2026 by mmphego Closed Mar 15, 2026

Description

The WebFetch tool fails with unable to get local issuer certificate when running behind a corporate SSL inspection proxy (Zscaler). This happens even when NODE_EXTRA_CA_CERTS is correctly configured and the CA certificate bundle is valid.

Environment

  • OS: macOS (Darwin 25.3.0, arm64)
  • Claude Code version: Latest (standalone Mach-O binary)
  • Node.js version: v25.6.1 (system — not used by Claude Code itself)
  • Proxy/SSL inspection: Zscaler (corporate SSL interception)

Root Cause Analysis

Through investigation, I found that the claude binary is compiled with the Bun runtime, not standard Node.js. The TLS certificate chain in the binary is:

1. Load bundled Mozilla CAs (from bun-usockets/src/crypto/root_certs.cpp)
2. IF NODE_USE_SYSTEM_CA === "1" → append system keychain certs
3. IF NODE_EXTRA_CA_CERTS is set → append extra certs from file

The WebFetch tool's internal HTTP client (bundled undici) creates its own dispatcher and does not properly inherit the Bun-patched CA store. The WebFetch code logs:

if (process.env.NODE_EXTRA_CA_CERTS)
    w("NODE_EXTRA_CA_CERTS detected - Node.js will automatically append to built-in CAs")

But this assumption is incorrect — it's running in Bun, not Node.js. The undici dispatcher bypasses Bun's global TLS context where the extra CAs were appended.

Reproduction Steps

  1. Be behind a corporate SSL inspection proxy (e.g., Zscaler)
  2. Set NODE_EXTRA_CA_CERTS to a valid PEM bundle containing the proxy's root CA
  3. Verify the cert bundle is valid: openssl s_client -connect www.linkedin.com:443 -CAfile $NODE_EXTRA_CA_CERTSVerify return code: 0 (ok)
  4. Verify plain Node.js works: node -e "fetch('https://www.linkedin.com').then(r => console.log(r.status))"200
  5. Use the WebFetch tool in Claude Code → Error: unable to get local issuer certificate

Evidence

| Method | Result |
|--------|--------|
| curl https://www.linkedin.com | 200 OK |
| node -e "https.get(...)" | 200 OK |
| node -e "fetch(...)" | 200 OK |
| openssl s_client -CAfile $NODE_EXTRA_CA_CERTS | Verify return code: 0 |
| WebFetch tool | unable to get local issuer certificate |
| Playwright browser tool | Works (uses Chromium/system keychain) |

Workaround

Setting NODE_USE_SYSTEM_CA=1 in the environment tells Bun to load certificates from the macOS system keychain (where the Zscaler root CA is already installed), bypassing the issue:

export NODE_USE_SYSTEM_CA=1

Suggested Fix

One or more of:

  1. Document NODE_USE_SYSTEM_CA=1 as the recommended setting for corporate/proxy environments
  2. Set NODE_USE_SYSTEM_CA=1 by default on macOS — the system keychain is the authoritative trust store and users expect it to work
  3. Fix the undici dispatcher in WebFetch to use Bun's patched TLS context (including NODE_EXTRA_CA_CERTS certs) rather than creating connections with default/bundled CAs only
  4. Pass the CA bundle explicitly to the undici Agent connect options when NODE_EXTRA_CA_CERTS is set, similar to how CLAUDE_CODE_CLIENT_CERT/CLAUDE_CODE_CLIENT_KEY are already handled for mTLS

Related

This likely affects all users behind corporate SSL inspection proxies (Zscaler, Netskope, Palo Alto, etc.) who rely on NODE_EXTRA_CA_CERTS.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗