[Bug] Claude in Chrome WebSocket bridge fails behind Zscaler/corporate TLS proxy (unable to get local issuer certificate)

Resolved 💬 2 comments Opened Mar 5, 2026 by dotbombjoe Closed Apr 2, 2026

Description

"Claude in Chrome" fails to connect when behind a Zscaler (or similar corporate TLS inspection) proxy. The WebSocket bridge to wss://bridge.claudeusercontent.com fails with unable to get local issuer certificate because the ws npm library used by the Electron app does not pick up the Zscaler root CA.

Disabling Zscaler resolves the issue immediately. The feature works perfectly with Zscaler off.

Environment

  • macOS (Darwin 25.3.0)
  • Claude Desktop v1.1.4498 (Electron 40.x)
  • Zscaler TLS inspection proxy active
  • Chrome extension v1.0.57 installed in active profile

Root Cause Analysis

The "Claude in Chrome" feature connects to wss://bridge.claudeusercontent.com/chrome/{userId} via the ws npm package (bh=require("ws") in the bundled index.js). The ws library uses Node.js's TLS stack (OpenSSL), which does not read the macOS system keychain for trusted CAs.

What we tried (none worked):

  1. NODE_EXTRA_CA_CERTS via launchctl setenv — Verified the env var is present in the Claude Desktop process (ps eww confirms it), but the ws WebSocket library still fails. Electron may initialize Node's TLS context before the env var is read, or it may be sandboxed from it.
  1. Adding Zscaler root CA to macOS admin-domain trust (sudo security add-trusted-cert -d -r trustRoot -p ssl -p basic -k /Library/Keychains/System.keychain) — macOS security verify-cert passes, and /usr/bin/curl (SecureTransport) succeeds, but Node.js/OpenSSL within Electron still rejects the cert.
  1. Zscaler CA already in System keychain with SSL + Basic trust policies in admin domain — still no effect on Electron's Node.js TLS.

What works:

  • Disabling Zscaler — bridge connects immediately
  • /usr/bin/curl (SecureTransport backend) — trusts the keychain, connects fine
  • "Control Chrome" MCP — works fine because it uses Electron's built-in Node.js for a local MCP server, not a remote WebSocket

Logs

[Claude in Chrome] Connecting to bridge: wss://bridge.claudeusercontent.com/chrome/da6b2600-...
[Claude in Chrome] Bridge WebSocket error after 922ms: unable to get local issuer certificate
[Claude in Chrome] Bridge connection closed (code: 1006, duration: 0ms)
chrome_bridge_connection_failed | error_type: 'websocket_error'

This repeats every ~30 seconds with exponential backoff.

Suggested Fixes

  1. Honor NODE_EXTRA_CA_CERTS — Ensure the env var is read and applied to Node's TLS context before any WebSocket connections are made. This is the standard Node.js mechanism for corporate proxy CAs.
  1. Use Chromium's network stack for the bridge WebSocket instead of Node.js ws — Chromium's network layer reads the macOS keychain natively and already trusts Zscaler CAs (Chrome browsing works fine behind Zscaler).
  1. Read macOS keychain CAs at startup — Use security find-certificate -a -p /Library/Keychains/System.keychain to export trusted CAs and inject them into Node's tls.rootCertificates.
  1. Add a Desktop setting for custom CA cert path, similar to how VS Code exposes http.proxyStrictSSL and http.certificateAuthority.

Impact

This affects any user running Claude Desktop behind a corporate TLS inspection proxy (Zscaler, Netskope, Symantec, etc.). "Claude in Chrome" is completely non-functional in these environments. "Control Chrome" MCP still works, so users have a partial workaround for basic browser automation, but the full "Claude in Chrome" side-panel experience is broken.

View original on GitHub ↗

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