Corporate Proxy and TLS Inspection for AI Coding Tools: Zscaler, WARP, and Netskope Fixes
AI coding tools are unusually sensitive to corporate networks. They hold long streaming connections open, they talk to several hosts, and they have to trust a certificate chain that a TLS-inspecting gateway such as Zscaler, Netskope, or Cloudflare WARP quietly replaces. The result is a family of confusing failures: certificate errors, connections that drop, agents that hang, and features that work at home but not at the office. This guide pulls together what Cursor, GitHub Copilot, and Claude Code each document, with a checklist for developers and a list of what to ask IT to change.
The short version. Two things break most often: the tool does not trust the gateway's certificate, and the gateway buffers or times out streaming responses. The cleanest fix for the second is to exclude the tools' domains from TLS inspection, which Cursor's documentation explicitly recommends. For the first, install the corporate root certificate in the operating system trust store, or point the tool at a CA file. Settings and host lists change, so check each vendor's current network page.
Why gateways and AI tools clash
- Certificate substitution. A TLS-inspecting gateway terminates your connection and re-signs it with its own CA. The tool will reject that certificate unless it trusts the gateway's root.
- Streaming gets buffered. Chat and agent responses arrive as a stream. Cursor's documentation says it defaults to HTTP/2 bidirectional streaming and falls back to HTTP/1.1 Server-Sent Events, mainly for proxies such as Zscaler that buffer or break HTTP/2 streams.
- Long connections get cut. Gateways that apply idle or maximum-duration timeouts kill a response that is still being generated.
- File scanning. Some gateways treat responses as downloads to scan, holding the stream open until the scan finishes.
- Many hosts. Each tool needs several domains for sign-in, updates, extensions, and models. Blocking one can break one feature while the rest works.
What each tool documents
| Tool | Proxy settings | Certificates | mTLS | Verify with |
|---|---|---|---|---|
| Claude Code | HTTPS_PROXY, HTTP_PROXY, NO_PROXY (lowercase variants too); no SOCKS support | Trusts bundled Mozilla CAs and the OS store by default; NODE_EXTRA_CA_CERTS for a custom CA; CLAUDE_CODE_CERT_STORE | CLAUDE_CODE_CLIENT_CERT, CLAUDE_CODE_CLIENT_KEY, optional passphrase | claude --debug and /status |
| Cursor | Works with most proxy configurations; no proxy environment variables documented on the network page | Recommends excluding Cursor domains from SSL inspection; no CA install steps on that page | Not documented there | Settings, Network, Run Diagnostics; curl tests |
| GitHub Copilot | HTTP proxy URLs only (not HTTPS); basic authentication and Kerberos supported | Install custom certificates in the OS trust store; found via win-ca (Windows), mac-ca (macOS), or OpenSSL bundle files (Linux) | Not documented there | curl to the Copilot ping endpoints; editor debug logs |
Symptoms, causes, and fixes
| Symptom | Cause | Fix |
|---|---|---|
SELF_SIGNED_CERT_IN_CHAIN, unable to get local issuer certificate (Claude Code) | TLS-inspection root not trusted by the runtime | Install the root in the OS store (and use a runtime that can read it) or set NODE_EXTRA_CA_CERTS; verify with claude --debug |
unable to verify the first certificate, certificate signature failure (Copilot) | Security software or proxy intercepting TLS with custom certificates | Ask IT for a proxy that does not intercept, or install the certificates in the OS trust store; add an exception for the IDE or extension |
GitHub Copilot could not connect to server. Extension activation failed: read ETIMEDOUT or read ECONNRESET | VPN, proxy, or firewall blocking the connection | Configure the proxy correctly (company-level too); test with curl to the ping endpoint |
Connection failed (Cursor) | HTTP/2 streaming broken or buffered by a proxy | HTTP/1.1 compatibility mode; exclude Cursor domains from inspection; see our Cursor connection guide |
| Cursor cloud agents stuck on Loading on cursor.com, desktop app fine | A secure web gateway (Cloudflare WARP, Zscaler, Netskope) scanning responses as downloads | Test with the gateway off or another network; ask IT to exclude cursor.com and *.cursor.com from TLS inspection and file scanning |
| Claude in Chrome cannot connect; rest of Claude Code works | bridge.claudeusercontent.com leaves via a different egress than claude.ai, so the org IP allowlist rejects it | Route it through the same egress (same Zscaler app segment or Netskope steering policy) |
| Requests fail through an inspecting proxy that mishandles compressed bodies (Claude Code) | Proxy mishandles gzip-compressed request bodies | Set CLAUDE_CODE_GZIP_REQUEST_BODIES=0 |
| Long responses stall or cut off (all tools) | Proxy buffering, idle timeouts, or forced timeouts on long-lived connections | Require SSE passthrough with no buffering and no forced timeouts; see our streaming failures guide |
Claude Code, step by step
- Set the proxy before launch. Claude Code reads proxy variables at startup, so a running session does not pick up later shell changes. It uses the first set of
https_proxy,HTTPS_PROXY,http_proxy,HTTP_PROXY. For proxies that need NTLM or Kerberos, Anthropic suggests an LLM gateway that supports your method. - Trust the certificate. The default trusts bundled Mozilla CAs plus the OS store. Reading the OS store needs a runtime with
tls.getCACertificates: the native installer always has it, and npm installs need Node 22.15 or later. Otherwise setNODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem. - Put variables in settings for background agents. Background sessions are hosted by a per-user supervisor that inherits the environment of whichever shell started it first, so Anthropic says to put proxy, CA, and mTLS variables in the
envblock of~/.claude/settings.jsonor managed settings. - Verify. Run
claude --debugand look for lines such asCA certs: Appended extra certificates from NODE_EXTRA_CA_CERTS. In a session,/statusshows the Proxy row and any mTLS rows. - Mind the watchdogs. Claude Code aborts streams that go quiet (for example a 180-second first-byte deadline on the direct API). A gateway that holds responses can trigger these, so fix buffering rather than only raising timeouts.
Cursor, step by step
- Run diagnostics. Cursor Settings, Network, Run Diagnostics.
- Check for inspection. Cursor's documentation suggests
curl -v https://api2.cursor.shand reading the issuer: Amazon RSA means no inspection; a vendor such as Zscaler means inspection is active. - Test streaming. The documentation describes a
curl --http1.1test against the SSE endpoint where output should arrive over about five seconds, and an HTTP/2 bidirectional test where output should appear once per second. Output arriving all at once means buffering. - Fall back. Set HTTP Compatibility Mode to HTTP/1.1 and restart while IT fixes the proxy.
- Ask IT to exclude the domains listed below from inspection, including roaming and off-network proxy profiles.
GitHub Copilot, step by step
- Use an HTTP proxy URL. GitHub says Copilot does not support a proxy URL that uses HTTPS. Basic authentication and Kerberos are supported; for Kerberos, make sure you have a valid ticket and the correct service principal name.
- Test connectivity.
curl --verbose https://copilot-proxy.githubusercontent.com/_pingshould return HTTP 200; through a proxy add-x http://YOUR-PROXY-URL:PORT. For Copilot Chat usehttps://api.githubcopilot.com/_ping. - Fix certificates properly. Install custom certificates in the OS trust store. GitHub lists ignoring certificate errors as an option (deselect Proxy Strict SSL in VS Code, or accept non-trusted certificates in a JetBrains IDE) but warns it can cause security issues and is not recommended.
- Add exceptions for security software that inspects secure traffic, for your IDE or the Copilot extension.
- Collect debug logs from the editor for IT or GitHub Support.
What to ask IT for
| Tool | Hosts to allow | Notes |
|---|---|---|
| Claude Code | api.anthropic.com, claude.ai, claude.com, platform.claude.com, sso.claude.com (SSO sign-in), downloads.claude.ai, mcp-proxy.anthropic.com (claude.ai MCP connectors), registry.npmjs.org (npm installs, plugins), github.com (plugin marketplaces), bridge.claudeusercontent.com (Claude in Chrome) | Telemetry hosts are optional; documented switches disable them |
| Cursor | *.cursor.sh, *.cursor-cdn.com, *.cursorapi.com, *.cursorvm.com, *.*.cursorvm.com, cursor.com, *.cursor.com; auth hosts including authenticate.cursor.sh, authenticator.cursor.sh, accounts.x.ai, accounts.spacex.ai | Explicit subdomains and update hosts are listed on Cursor's network configuration page |
| GitHub Copilot | https://*.individual.githubcopilot.com, https://*.business.githubcopilot.com, https://*.enterprise.githubcopilot.com, or https://*.githubcopilot.com/* for all plans; apex github.com; use gh api meta for the rest | Do not add plan-specific wildcards if you use subscription-based network routing |
For Cursor, the documentation recommends excluding its domains from SSL inspection because its services are already encrypted end to end. If inspection must cover all traffic, the proxy needs working HTTP/2 streaming or HTTP/1.1 fallback, SSE passthrough without buffering, long-lived connections without forced timeouts, and no buffering of streaming content types. For Claude Code, if your organization uses IP allowlisting, route bridge.claudeusercontent.com through the same egress as claude.ai and api.anthropic.com. Check each vendor's current page for the full lists; they change.
A script that checks the usual suspects
The script below checks a Claude Code environment for the proxy and certificate mistakes Anthropic's documentation warns about, flags an HTTPS proxy URL for Copilot, interprets a curl issuer line to tell whether a gateway is inspecting TLS, and detects buffering from the arrival times of streamed chunks. It never prints credentials. Its tests run without any of the tools installed.
import os
from dataclasses import dataclass
from urllib.parse import urlparse
INSPECTOR_VENDORS = ("zscaler", "netskope", "cloudflare", "palo alto", "fortinet", "forcepoint",
"symantec", "broadcom", "blue coat", "cisco", "checkpoint", "sophos", "mcafee", "skyhigh")
@dataclass
class Finding:
level: str # error | warn | info
where: str
message: str
fix: str
def proxy_for_claude(env: dict) -> str:
"""Claude Code uses the first of https_proxy, HTTPS_PROXY, http_proxy, HTTP_PROXY that is set."""
for k in ("https_proxy", "HTTPS_PROXY", "http_proxy", "HTTP_PROXY"):
if env.get(k):
return env[k]
return ""
def check_claude_env(env: dict, exists=os.path.exists) -> list:
out = []
p = proxy_for_claude(env)
if p:
u = urlparse(p)
if u.scheme.startswith("socks"):
out.append(Finding("error", "proxy", "Claude Code does not support SOCKS proxies",
"Use an HTTP(S) proxy, or put an LLM gateway in front."))
elif u.scheme not in ("http", "https") or not u.hostname:
out.append(Finding("error", "proxy", f"proxy URL {p!r} is missing a scheme or host",
"Use a form like http://proxy.example.com:8080; Claude Code stops launch on an unparsable proxy URL."))
if u.username and u.password:
out.append(Finding("warn", "proxy", "credentials are embedded in the proxy URL",
"Avoid hardcoding passwords in scripts; use secure credential storage."))
ca = env.get("NODE_EXTRA_CA_CERTS")
if ca and not exists(ca):
out.append(Finding("error", "NODE_EXTRA_CA_CERTS", f"file not found: {ca}",
"Point it at your company CA bundle (PEM)."))
if p and not ca:
out.append(Finding("info", "tls", "a proxy is set with no NODE_EXTRA_CA_CERTS",
"Fine if the inspection root is in the OS trust store; otherwise set NODE_EXTRA_CA_CERTS."))
store = env.get("CLAUDE_CODE_CERT_STORE")
if store:
bad = [s for s in store.split(",") if s.strip() not in ("bundled", "system")]
if bad:
out.append(Finding("error", "CLAUDE_CODE_CERT_STORE", f"unknown source(s): {', '.join(bad)}",
"Use bundled, system, or bundled,system."))
elif "system" not in store:
out.append(Finding("warn", "CLAUDE_CODE_CERT_STORE", "the OS trust store is excluded",
"A corporate inspection root installed in the OS store will not be trusted."))
cert, key = env.get("CLAUDE_CODE_CLIENT_CERT"), env.get("CLAUDE_CODE_CLIENT_KEY")
if bool(cert) != bool(key):
out.append(Finding("error", "mTLS", "client certificate and key must be set together",
"Set both CLAUDE_CODE_CLIENT_CERT and CLAUDE_CODE_CLIENT_KEY."))
return out
def check_copilot_proxy(proxy_url: str) -> list:
if urlparse(proxy_url).scheme == "https":
return [Finding("error", "proxy", "Copilot does not support a proxy URL that uses HTTPS",
"Use an HTTP proxy URL, for example http://proxy.example.com:8080.")]
return []
def issuer_verdict(issuer_line: str) -> str:
s = issuer_line.lower()
if "amazon" in s:
return "no inspection (public issuer)"
for v in INSPECTOR_VENDORS:
if v in s:
return f"inspection likely active ({v})"
return "unknown issuer: may be inspection or a different public CA; compare with a network that does not inspect"
def is_buffered(arrivals: list, expected_span: float, tolerance: float = 0.3) -> bool:
"""arrivals: seconds since request start for each streamed chunk. A stream that should take
expected_span seconds but arrives within tolerance*expected_span is being buffered."""
if len(arrivals) < 2:
return False
return (max(arrivals) - min(arrivals)) < tolerance * expected_span
def lvl(fs):
return [(f.level, f.where) for f in fs]
def test_proxy_precedence_order():
env = {"HTTP_PROXY": "http://c:1", "https_proxy": "http://a:1", "HTTPS_PROXY": "http://b:1"}
assert proxy_for_claude(env) == "http://a:1"
assert proxy_for_claude({"HTTP_PROXY": "http://c:1"}) == "http://c:1"
assert proxy_for_claude({}) == ""
def test_socks_and_missing_scheme_are_errors():
assert ("error", "proxy") in lvl(check_claude_env({"HTTPS_PROXY": "socks5://p:1080"}, lambda p: True))
assert ("error", "proxy") in lvl(check_claude_env({"HTTPS_PROXY": "proxy.example.com:8080"}, lambda p: True))
assert not any(f.level == "error" for f in check_claude_env({"HTTPS_PROXY": "http://p:8080", "NODE_EXTRA_CA_CERTS": "/ca"}, lambda p: True))
def test_embedded_credentials_warn():
f = check_claude_env({"HTTPS_PROXY": "http://u:pw@p:8080"}, lambda p: True)
assert ("warn", "proxy") in lvl(f)
assert all("pw" not in x.message + x.fix for x in f)
def test_ca_file_and_info_note():
assert ("error", "NODE_EXTRA_CA_CERTS") in lvl(check_claude_env({"NODE_EXTRA_CA_CERTS": "/nope"}, lambda p: False))
assert ("info", "tls") in lvl(check_claude_env({"HTTPS_PROXY": "http://p:1"}, lambda p: False))
def test_cert_store_and_mtls_pairing():
assert ("error", "CLAUDE_CODE_CERT_STORE") in lvl(check_claude_env({"CLAUDE_CODE_CERT_STORE": "bundled,foo"}, lambda p: True))
assert ("warn", "CLAUDE_CODE_CERT_STORE") in lvl(check_claude_env({"CLAUDE_CODE_CERT_STORE": "bundled"}, lambda p: True))
assert check_claude_env({"CLAUDE_CODE_CERT_STORE": "bundled,system"}, lambda p: True) == []
assert ("error", "mTLS") in lvl(check_claude_env({"CLAUDE_CODE_CLIENT_CERT": "/c.pem"}, lambda p: True))
def test_copilot_rejects_https_proxy_urls():
assert check_copilot_proxy("https://proxy:8080")[0].level == "error"
assert check_copilot_proxy("http://proxy:8080") == []
def test_issuer_verdicts():
assert issuer_verdict("issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M02").startswith("no inspection")
assert "zscaler" in issuer_verdict("issuer: O=Zscaler Inc.; CN=Zscaler Intermediate Root CA")
assert issuer_verdict("issuer: CN=Some Corp CA").startswith("unknown issuer")
def test_buffering_detection():
steady = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0]
held = [5.0, 5.0, 5.01, 5.02, 5.02, 5.03]
assert is_buffered(steady, expected_span=5.0) is False
assert is_buffered(held, expected_span=5.0) is True
assert is_buffered([1.0], expected_span=5.0) is False
What not to do
- Do not disable certificate verification as a fix. Install the corporate root or request an exclusion instead.
- Do not put proxy passwords in scripts or shell history. Anthropic warns against hardcoding them.
- Do not rely on a shell export for background agents. Put the variables in settings.
- Do not only raise timeouts. If the gateway buffers the stream, a longer timeout just waits longer for nothing.
Related reading: Cursor vs Claude Code vs Copilot errors and Top Claude Code errors.
Sources
- Claude Code documentation: Enterprise network configuration
- Cursor documentation: Network configuration
- Cursor documentation: Common issues
- GitHub Docs: Troubleshooting network errors for GitHub Copilot
- GitHub Docs: Copilot allowlist reference
Frequently asked questions
Why do AI coding tools break behind Zscaler, Netskope, or Cloudflare WARP?
These tools stream long-lived responses, and security gateways that inspect TLS can buffer, time out, or break those streams, or can present a certificate the tool does not trust. Cursor's documentation says it defaults to HTTP/2 bidirectional streaming and falls back to HTTP/1.1 Server-Sent Events, mainly for proxies such as Zscaler that buffer or break HTTP/2 streams. Copilot and Claude Code document certificate and proxy settings for the other half of the problem.
Does Claude Code support SOCKS proxies?
No. Anthropic's network configuration page says Claude Code does not support SOCKS proxies. It respects HTTPS_PROXY and HTTP_PROXY, plus lowercase variants, and for proxies that need NTLM or Kerberos authentication it suggests using an LLM gateway that supports your method.
How do I make Claude Code trust my company's TLS-inspection certificate?
By default Claude Code trusts both its bundled Mozilla CA certificates and your operating system's certificate store, so a proxy whose root certificate is in the OS trust store should work without extra configuration, provided the runtime can read it. For a custom CA file, set NODE_EXTRA_CA_CERTS to its path. Start Claude Code with claude --debug and look for the log line confirming the extra certificates were appended.
Does GitHub Copilot work with an HTTPS proxy URL?
GitHub's network error troubleshooting page says Copilot does not support a proxy URL that uses HTTPS; only HTTP proxy URLs work. It supports basic authentication and Kerberos for proxies. For certificates, install your company's custom certificates in the operating system trust store, since Copilot finds them through OS-level sources.
Should I just turn off certificate verification?
No. GitHub documents the option to deselect Proxy Strict SSL in VS Code, or accept non-trusted certificates in a JetBrains IDE, but says it can cause security issues and is not recommended. Install the corporate CA properly, or ask IT to exclude the tool's domains from TLS inspection.
What should IT exclude from TLS inspection for Cursor?
Cursor's enterprise network documentation recommends disabling SSL inspection for its domains, listing .cursor.sh, cursor-cdn.com, marketplace.cursorapi.com, the authentication hosts, and the cursorvm.com patterns, plus the sign-in hosts accounts.spacex.ai and accounts.x.ai. It says exclusions should also apply to roaming and off-network proxy profiles. If inspection must cover all traffic, the proxy needs working HTTP/2 streaming or HTTP/1.1 fallback, SSE passthrough without buffering, and no forced timeouts on long-lived connections.
How can I tell whether my proxy is inspecting TLS?
Cursor's documentation suggests running curl -v against api2.cursor.sh and checking the certificate issuer: an Amazon RSA issuer indicates no inspection, while a proxy vendor such as Zscaler as issuer indicates inspection is active. The same check works for other hosts, though the expected public issuer differs by host.
How can I tell whether my proxy is buffering streaming responses?
Send a streaming request and watch when the output arrives. Cursor's documentation describes tests where output should appear steadily over several seconds; if it all arrives at once at the end, the proxy is buffering. The script in this guide applies the same idea to arrival timestamps.
Why does Cursor's cloud agent page load forever while the desktop app works?
Cursor's documentation says a secure web gateway, such as Cloudflare WARP, Zscaler, or Netskope, is likely scanning cursor.com responses as downloads and holding the agent stream open. Confirm by turning off the gateway client or trying another network, then ask IT to exclude cursor.com and *.cursor.com from TLS inspection and file scanning.
Why does Claude in Chrome fail when the rest of Claude Code works?
Anthropic's documentation says that if your organization uses IP allowlisting, bridge.claudeusercontent.com must go out through the same proxy egress as claude.ai and api.anthropic.com, for example in the same Zscaler app segment or Netskope steering policy. Otherwise the connection is checked against the allowlist from the wrong address and the extension cannot connect.