[BUG] No TLS configuration escape hatch — Bun runtime ignores NODE_OPTIONS, corporate firewalls block connections
Summary
Claude Code's embedded Bun runtime ignores \NODE_OPTIONS\ (e.g., \--tls-max-v1.2\), leaving users with no way to configure TLS behavior. This makes Claude Code unusable on:
- Corporate networks with JA3/JA4 TLS fingerprint-based firewalls (#21652)
- WSL2 environments with TLS 1.3 decode errors (#34686)
Curl, Node.js, and browsers work fine on these networks — only Bun is blocked.
Root Cause
- Corporate TLS fingerprinting: Palo Alto, Fortinet, Zscaler firewalls identify connections by TLS Client Hello fingerprint (JA3/JA4). Bun's TLS fingerprint differs from standard browsers/Node.js, triggering RST
- WSL2 TLS 1.3 bug: WSL2's network stack has a known issue with TLS 1.3 decode errors (\
alert 50\). \NODE_OPTIONS="--tls-max-v1.2"\fixes Node.js apps but is ignored by Bun - No escape hatch: No Claude Code-specific env var or CLI flag to control TLS version or cipher suites
Evidence from #21652:
| Test | Result |
|------|--------|
| curl to api.anthropic.com | HTTP 404 (works) |
| Node.js v18/v20 fetch | HTTP 404 (works) |
| Claude Code (Bun) | ECONNRESET (blocked) |
| Mobile tethering | Works |
| VPN | Works |
Proposed Fix
Add Claude Code-specific TLS configuration:
import tls from 'tls';
function configureTLS() {
const maxVersion = process.env.CLAUDE_CODE_TLS_MAX_VERSION;
if (maxVersion && ['TLSv1.2', 'TLSv1.3'].includes(maxVersion)) {
tls.DEFAULT_MAX_VERSION = maxVersion;
debug(\`TLS max version: \${maxVersion}\`);
}
const ciphers = process.env.CLAUDE_CODE_TLS_CIPHERS;
if (ciphers) {
tls.DEFAULT_CIPHERS = ciphers;
debug('TLS ciphers overridden');
}
const minVersion = process.env.CLAUDE_CODE_TLS_MIN_VERSION;
if (minVersion && ['TLSv1.2', 'TLSv1.3'].includes(minVersion)) {
tls.DEFAULT_MIN_VERSION = minVersion;
}
}
// CLI flag for convenience:
// claude --tls-compat
// Equivalent to CLAUDE_CODE_TLS_MAX_VERSION=TLSv1.2
// Diagnostics in --verbose:
function logTLSDiagnostics() {
debug(\`TLS: max=\${tls.DEFAULT_MAX_VERSION} min=\${tls.DEFAULT_MIN_VERSION}\`);
debug(\`TLS: ciphers=\${tls.DEFAULT_CIPHERS ? 'custom' : 'default'}\`);
debug(\`TLS: CA=\${process.env.NODE_EXTRA_CA_CERTS || 'system'}\`);
debug(\`Runtime: \${typeof Bun !== 'undefined' ? 'Bun' : 'Node.js'}\`);
}
Environment Variables
| Variable | Values | Default | Purpose |
|----------|--------|---------|---------|
| \CLAUDE_CODE_TLS_MAX_VERSION\ | \TLSv1.2\, \TLSv1.3\ | \TLSv1.3\ | Cap TLS version |
| \CLAUDE_CODE_TLS_MIN_VERSION\ | \TLSv1.2\, \TLSv1.3\ | \TLSv1.2\ | Floor TLS version |
| \CLAUDE_CODE_TLS_CIPHERS\ | OpenSSL cipher string | system default | Override cipher suites |
CLI Flag
\claude --tls-compat\ — shortcut for \CLAUDE_CODE_TLS_MAX_VERSION=TLSv1.2\. Most users won't know which ciphers to set; this provides a one-step fix.
Testing Plan
Unit Tests
| Test | Input | Expected |
|------|-------|----------|
| Sets max version | \CLAUDE_CODE_TLS_MAX_VERSION=TLSv1.2\ | \tls.DEFAULT_MAX_VERSION\ = 'TLSv1.2' |
| Sets ciphers | \CLAUDE_CODE_TLS_CIPHERS=...\ | Custom ciphers applied |
| No-op without env vars | No vars set | Defaults unchanged |
| Invalid version rejected | \=SSLv3\ | Warning logged, default kept |
| \--tls-compat\ parsed | CLI flag | TLS 1.2 max set |
| Diagnostics output | \logTLSDiagnostics()\ | Shows version, ciphers, CA, runtime |
Integration Tests
| Test | Setup | Expected |
|------|-------|----------|
| API call with TLS 1.2 | Set max=TLSv1.2 | Connects with TLS 1.2 |
| API call with default | No vars | Connects with TLS 1.3 |
| Config applies to all connections | Set vars | API + OTEL + MCP all use configured TLS |
| Flag + env var precedence | Both set | CLI flag wins |
E2E Tests
| Test | Steps | Expected |
|------|-------|----------|
| WSL2 workaround | WSL2 + \CLAUDE_CODE_TLS_MAX_VERSION=TLSv1.2\ | Connects successfully |
| Corporate firewall | Network with JA3 blocking + \--tls-compat\ | Connects |
| No regression | Default config, normal usage | Works as before |
Related Issues
- #21652 — ECONNRESET on corporate network (TLS fingerprinting)
- #34686 — ECONNRESET on WSL2 (TLS 1.3 decode error)
- #4297 — API Error (Connection error.) on Windows
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗