You install Claude Code (or Codex, or Gemini CLI) on your work laptop, run it, and instead of generating code it spits out unable to get local issuer certificate or self-signed certificate in chain. Meanwhile curl https://api.anthropic.com works fine. If that sounds familiar, you're not dealing with a broken tool — you're dealing with your employer's network, and the same handful of fixes apply across every AI coding CLI on the market.
This is a troubleshooting guide for that exact situation: corporate proxies, TLS inspection (Zscaler, CrowdStrike Falcon, and friends), and custom CA certificates. The mechanics are shared, but each tool exposes them slightly differently, and one popular tool — Codex — needs an entirely separate set of knobs.
The three problems, in order
Almost every enterprise failure is one of three things:
- No route out. The CLI can't reach the internet because traffic must go through a proxy it doesn't know about. Symptoms:
ETIMEDOUT,ECONNREFUSED, or hangs. - TLS inspection. Your proxy decrypts HTTPS and re-signs it with a private root CA the CLI doesn't trust. Symptoms:
unable to get local issuer certificate,SELF_SIGNED_CERT_IN_CHAIN,unable to verify the first certificate,UNABLE_TO_GET_ISSUER_CERT_LOCALLY. - Auth or blocking. The proxy demands credentials (
HTTP 407) or the firewall blocks a required domain (HTTP 403).
Diagnose in that order. There's no point fighting certificates if packets aren't leaving the machine.
Proxy configuration
Every one of these CLIs respects the standard proxy environment variables. Claude Code's docs are explicit about it, and the others follow the same convention:
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"
HTTPS_PROXY is the one that matters most — these tools talk HTTPS. For Claude Code, NO_PROXY accepts space- or comma-separated host lists, and NO_PROXY="*" bypasses the proxy entirely. Always include localhost and 127.0.0.1 in NO_PROXY, especially for tools that use a loopback OAuth callback (more on that below).
For a basic-auth proxy, put the credentials in the URL and URL-encode special characters:
# @ in the password becomes %40
export HTTPS_PROXY=http://username:p%40ssword@proxy.example.com:8080
A few hard limits worth knowing up front:
- Claude Code does not support SOCKS proxies. Only HTTP/HTTPS. Don't waste time on a SOCKS5 endpoint.
- NTLM/Kerberos proxies are not handled natively. If your proxy uses Windows Integrated Auth and returns 407, you need a local shim —
cntlmorpx— that does the NTLM handshake and exposes a no-auth local HTTP proxy. PointHTTPS_PROXYat that local port. Claude Code's official guidance for this case is to use an LLM gateway that supports your auth method.
TLS inspection and CA certificates
This is where most of the pain lives. When Zscaler or Falcon inspects TLS, it presents a certificate signed by your company's private root CA. The CLI has to trust that CA.
Here's the critical split: Node-based CLIs and Rust-based CLIs use different variables.
| CLI | Runtime | CA variable | Proxy |
|---|---|---|---|
| Claude Code | Bun | NODE_EXTRA_CA_CERTS, NODE_USE_SYSTEM_CA=1, CLAUDE_CODE_CERT_STORE | HTTPS_PROXY (no SOCKS) |
| Codex CLI | Rust (reqwest) | CODEX_CA_CERTIFICATE, falls back to SSL_CERT_FILE | HTTPS_PROXY |
| Gemini CLI | Node | NODE_USE_SYSTEM_CA=1, then NODE_EXTRA_CA_CERTS | HTTPS_PROXY |
| Antigravity | Node (Electron) | NODE_EXTRA_CA_CERTS + OS trust store | HTTPS_PROXY |
| Qwen Code | Node | NODE_EXTRA_CA_CERTS | qwen --proxy <url> or settings.json |
The single most common mistake: trying
NODE_EXTRA_CA_CERTSon Codex. It's Rust, not Node, so the Node variables — includingNODE_TLS_REJECT_UNAUTHORIZED=0— are silently ignored. UseCODEX_CA_CERTIFICATE.
Getting your company's root CA
Ask your IT/security team for the corporate root CA in PEM format (.pem or .crt). If it's already installed in your OS trust store, you can often export it from there (Keychain Access on macOS, certmgr.msc on Windows). You want the root CA, not the leaf — a full PEM bundle is fine.
Claude Code
By default Claude Code trusts both its bundled Mozilla CA set and the OS certificate store, so if IT installed the Zscaler/Falcon root in your OS trust store, it should just work. CLAUDE_CODE_CERT_STORE controls which stores it uses (default bundled,system).
If the CA isn't in the OS store, point at a PEM file directly:
export NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem
Two gotchas specific to Claude Code:
WebFetchcan still fail withunable to get local issuer certificateeven whenNODE_EXTRA_CA_CERTSis set correctly. Claude Code runs on Bun, and WebFetch's internal undici dispatcher doesn't inherit Bun's patched TLS context — socurl,node fetch, andopensslall succeed while WebFetch alone fails. The fix isexport NODE_USE_SYSTEM_CA=1, which loads CAs from the OS/keychain store.- Set the variable in your shell, not just
settings.json.NODE_EXTRA_CA_CERTSset inside~/.claude/settings.jsonmay not take effect for every code path; the actual process environment is more reliable.
Claude Code also supports mTLS / client-certificate auth if your proxy requires it, via CLAUDE_CODE_CLIENT_CERT, CLAUDE_CODE_CLIENT_KEY, and CLAUDE_CODE_CLIENT_KEY_PASSPHRASE (the passphrase only for encrypted keys).
Codex CLI
Set the CA before logging in — the same setting covers login, normal HTTPS requests, and secure WebSocket connections:
export CODEX_CA_CERTIFICATE=/path/to/corporate-root-ca.pem
codex login
Historically Codex failed OAuth token exchange behind inspecting proxies (Token exchange failed: error sending request for url https://auth.openai.com/oauth/token) because its reqwest client only trusted system default roots. CODEX_CA_CERTIFICATE (or SSL_CERT_FILE) is the fix for that. If CODEX_CA_CERTIFICATE is unset, Codex falls back to SSL_CERT_FILE.
Gemini CLI
Official guidance: try NODE_USE_SYSTEM_CA=1 first (it reads the OS native cert store), and if that doesn't resolve UNABLE_TO_GET_ISSUER_CERT_LOCALLY, add NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.crt.
Qwen Code
Node-based, so NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.crt handles the inspection CA. Errors you'll see include UNABLE_TO_GET_ISSUER_CERT_LOCALLY, UNABLE_TO_VERIFY_LEAF_SIGNATURE, and Device authorization flow failed: fetch failed. Proxy is set with the qwen --proxy <url> flag or the proxy key in settings.json.
Antigravity (Google)
Antigravity is the awkward one because it's a GUI app. You need the trifecta: HTTPS_PROXY/HTTP_PROXY/NO_PROXY (with localhost and 127.0.0.1 in NO_PROXY), NODE_EXTRA_CA_CERTS for the corporate CA, and the CA trusted in the OS store. On macOS:
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain corporate-ca.pem
Critically, launch Antigravity from the same terminal that exported the variables — opening it from the Dock or Start Menu won't inherit them. Its OAuth sign-in can also break when endpoint protection blocks loopback traffic to the callback port (localhost:9004); proxy and http.proxyStrictSSL settings live in .antigravity/settings.json.
The "works in terminal, not in the app" trap
This deserves its own callout because it burns a lot of people. GUI apps started from the Dock/Start Menu don't inherit shell-exported env vars. So your NODE_EXTRA_CA_CERTS and HTTPS_PROXY reach the CLI but not the Electron app or a desktop "Code pane." Claude Desktop has documented bugs where it fails to forward NODE_EXTRA_CA_CERTS to the spawned Claude Code subprocess, producing Self-signed certificate detected errors. The durable fix for any GUI tool is to install the CA in the OS trust store rather than relying on env vars.
Verifying your setup
Before you start changing tool configs, confirm what's actually happening:
# Is the proxy even set?
env | grep -i proxy
# Does the proxy work for a known endpoint?
curl -x "$HTTPS_PROXY" https://api.anthropic.com
# What CA chain is the proxy presenting?
openssl s_client -connect api.anthropic.com:443 -showcerts
If curl and openssl succeed but the CLI fails, you've isolated it to the tool's CA handling — which means it's a NODE_USE_SYSTEM_CA / NODE_EXTRA_CA_CERTS / CODEX_CA_CERTIFICATE problem, not a network one.
Domains to allowlist
If you're hitting HTTP 403, get these added. For Claude Code, the required allowlist includes api.anthropic.com (API), claude.ai and platform.claude.com (auth), downloads.claude.ai (plugins/installer), bridge.claudeusercontent.com (Claude in Chrome WebSocket), and raw.githubusercontent.com (release notes). Codex auth needs auth.openai.com. The other tools have their own auth and API domains — capture them from a failing run and hand the list to your network team.
The escape hatch: a central AI gateway
Configuring five tools across a fleet of laptops doesn't scale. The alternative is to route every AI CLI through an internal LLM/AI gateway — Kong AI Gateway, WSO2, or LiteLLM — that handles the corporate proxy, auth, and CA termination centrally. This is the recommended path for NTLM/Kerberos environments where per-tool config is painful or impossible, and it gives security a single chokepoint to monitor. If your hesitation is partly about code leaving the building at all, a local-first option like Wide Area AI exposes an OpenAI-compatible endpoint that serves inference from your own hardware first and only fails over to cloud providers when local nodes are unavailable — you point each CLI's base URL at the gateway and keep most requests (and their per-token cost) on-premises.
Bottom line
- Triage in order: route (
ETIMEDOUT) → TLS (SELF_SIGNED_CERT_IN_CHAIN) → auth/block (407/403). - Proxy is universal:
HTTPS_PROXYwith URL-encoded creds for basic auth; a localcntlm/pxshim for NTLM/Kerberos; no SOCKS for Claude Code. - CA certs split by runtime: Node CLIs (Claude Code, Gemini, Qwen, Antigravity) use
NODE_USE_SYSTEM_CA=1thenNODE_EXTRA_CA_CERTS; Codex (Rust) usesCODEX_CA_CERTIFICATE/SSL_CERT_FILE. - Install the corporate CA in the OS trust store to fix GUI apps that ignore env vars.
- Never leave
NODE_TLS_REJECT_UNAUTHORIZED=0on — debug-only, and useless for Codex anyway. - At scale, a central AI gateway beats configuring every tool on every machine.
Get the route, the CA, and the auth right and these tools behave identically to any other HTTPS client on your network — the failures just look scarier because the error strings are unfamiliar.