Corporate Proxy and TLS Inspection for AI Coding Tools: Zscaler, WARP, and Netskope Fixes | AI Error Hub
AI Error Hub/Blog/Corporate Proxy and TLS Inspection
Networking · Troubleshooting

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.

ZK
Zain Khan
Editor, AI Error Hub · November 10, 2026 · 16 min read

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

ToolProxy settingsCertificatesmTLSVerify with
Claude CodeHTTPS_PROXY, HTTP_PROXY, NO_PROXY (lowercase variants too); no SOCKS supportTrusts bundled Mozilla CAs and the OS store by default; NODE_EXTRA_CA_CERTS for a custom CA; CLAUDE_CODE_CERT_STORECLAUDE_CODE_CLIENT_CERT, CLAUDE_CODE_CLIENT_KEY, optional passphraseclaude --debug and /status
CursorWorks with most proxy configurations; no proxy environment variables documented on the network pageRecommends excluding Cursor domains from SSL inspection; no CA install steps on that pageNot documented thereSettings, Network, Run Diagnostics; curl tests
GitHub CopilotHTTP proxy URLs only (not HTTPS); basic authentication and Kerberos supportedInstall custom certificates in the OS trust store; found via win-ca (Windows), mac-ca (macOS), or OpenSSL bundle files (Linux)Not documented therecurl to the Copilot ping endpoints; editor debug logs

Symptoms, causes, and fixes

SymptomCauseFix
SELF_SIGNED_CERT_IN_CHAIN, unable to get local issuer certificate (Claude Code)TLS-inspection root not trusted by the runtimeInstall 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 certificatesAsk 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 ECONNRESETVPN, proxy, or firewall blocking the connectionConfigure the proxy correctly (company-level too); test with curl to the ping endpoint
Connection failed (Cursor)HTTP/2 streaming broken or buffered by a proxyHTTP/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 fineA secure web gateway (Cloudflare WARP, Zscaler, Netskope) scanning responses as downloadsTest 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 worksbridge.claudeusercontent.com leaves via a different egress than claude.ai, so the org IP allowlist rejects itRoute 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 bodiesSet CLAUDE_CODE_GZIP_REQUEST_BODIES=0
Long responses stall or cut off (all tools)Proxy buffering, idle timeouts, or forced timeouts on long-lived connectionsRequire SSE passthrough with no buffering and no forced timeouts; see our streaming failures guide

Claude Code, step by step

  1. 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.
  2. 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 set NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem.
  3. 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 env block of ~/.claude/settings.json or managed settings.
  4. Verify. Run claude --debug and look for lines such as CA certs: Appended extra certificates from NODE_EXTRA_CA_CERTS. In a session, /status shows the Proxy row and any mTLS rows.
  5. 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

  1. Run diagnostics. Cursor Settings, Network, Run Diagnostics.
  2. Check for inspection. Cursor's documentation suggests curl -v https://api2.cursor.sh and reading the issuer: Amazon RSA means no inspection; a vendor such as Zscaler means inspection is active.
  3. Test streaming. The documentation describes a curl --http1.1 test 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.
  4. Fall back. Set HTTP Compatibility Mode to HTTP/1.1 and restart while IT fixes the proxy.
  5. Ask IT to exclude the domains listed below from inspection, including roaming and off-network proxy profiles.

GitHub Copilot, step by step

  1. 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.
  2. Test connectivity. curl --verbose https://copilot-proxy.githubusercontent.com/_ping should return HTTP 200; through a proxy add -x http://YOUR-PROXY-URL:PORT. For Copilot Chat use https://api.githubcopilot.com/_ping.
  3. 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.
  4. Add exceptions for security software that inspects secure traffic, for your IDE or the Copilot extension.
  5. Collect debug logs from the editor for IT or GitHub Support.

What to ask IT for

ToolHosts to allowNotes
Claude Codeapi.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.aiExplicit subdomains and update hosts are listed on Cursor's network configuration page
GitHub Copilothttps://*.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 restDo 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

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.

Keep reading