[BUG] Remote Control bridge registration sent as plaintext HTTP through proxy instead of CONNECT-tunneling
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
Remote Control fails to register whenever an HTTP proxy is configured via HTTPS_PROXY / HTTP_PROXY. claude remote-control (and --remote-control, and remoteControlAtStartup: true) aborts at registration with an HTTP 400.
The Remote Control bridge client does not CONNECT-tunnel HTTPS through the proxy. It sends the registration request to the proxy in forward-proxy absolute-form over a cleartext connection, so the upstream receives plaintext and rejects it. The normal Anthropic API client works fine over the same proxy (it CONNECT-tunnels correctly), so only the Remote Control bridge path is broken.
Side effect: the Authorization: Bearer token is transmitted in cleartext to the proxy and onward (the error message itself warns about this). With remoteControlAtStartup: true, this fires on every session start.
What Should Happen?
The bridge client should honor HTTPS_PROXY by issuing CONNECT api.anthropic.com:443 and then performing TLS to the origin, exactly as the main Anthropic API client already does. Registration should succeed through an HTTP proxy, and the bearer token should never be sent in cleartext.
Error Messages/Logs
Error: Registration: Failed with status 400: This request was sent over HTTP. Only HTTPS is supported for API use. Consider revoking your API key, as it was sent in plaintext over the internet with this request.
# What the bridge client actually sends to the proxy, captured by pointing
# HTTPS_PROXY at a local logging listener:
POST https://api.anthropic.com/v1/environments/bridge HTTP/1.1
Authorization: Bearer sk-ant-oat01-<REDACTED>
anthropic-version: 2023-06-01
anthropic-beta: environments-2025-11-01
x-environment-runner-version: 2.1.193
User-Agent: claude
# Expected instead: a CONNECT tunnel -> CONNECT api.anthropic.com:443 (then TLS to origin)
Steps to Reproduce
- Start any HTTP forward proxy locally, e.g. tinyproxy / squid / mitmproxy on
127.0.0.1:8888(or a tiny TCP listener that logs the first bytes, to inspect what the client emits). - Launch Remote Control through it:
````
HTTPS_PROXY=http://127.0.0.1:8888 HTTP_PROXY=http://127.0.0.1:8888 \
claude remote-control --spawn=same-dir
- Registration fails immediately with the HTTP 400 shown above.
- To confirm the cause: point
HTTPS_PROXYat a listener that logs the first request bytes. The client sendsPOST https://api.anthropic.com/v1/environments/bridge HTTP/1.1(absolute-form, cleartext) instead ofCONNECT api.anthropic.com:443.
Notes:
- The normal API path over the same proxy works (it CONNECT-tunnels), so this is specific to the bridge client.
- Tried and did not help:
NODE_USE_ENV_PROXY=1;HTTPS_PROXYonly (withHTTP_PROXYunset);ALL_PROXY. All reproduce the identical error.
Claude Model
Not sure / Multiple models (not model-related; this is a networking/registration bug).
Is this a regression?
No, this never worked.
Claude Code Version
2.1.193 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other (terminal-independent; reproduces in a non-interactive invocation too).
Additional Information
- Root cause is in the bridge client's proxy handling, not the proxy itself: a forward HTTP proxy correctly CONNECT-tunnels the main client's HTTPS to
api.anthropic.com; only the/v1/environments/bridgeregistration is emitted in absolute-form cleartext. - Security impact worth prioritizing: the OAuth bearer token (
sk-ant-oat01-...) is sent in cleartext, andremoteControlAtStartup: truetriggers this on every session start. - Searched existing issues before filing. Closest are #65801 (distinct: bridge is reachable, token rejected upstream) and #1382 (old main-client corporate-proxy 400, since fixed). This report is specifically the Remote Control bridge client not CONNECT-tunneling through an HTTP proxy.
3 Comments
Not a maintainer — flagging the containment step, because the diagnosis here is solid and the security impact is bigger than a failed registration.
The token is already compromised — treat it as exposed and rotate it now. Your capture shows the full
Authorization: Bearer sk-ant-oat01-…going out in cleartext to the proxy (and the 400 from the origin literally tells you to revoke it). That OAuth access token has now traversed at least the proxy hop in the clear; withremoteControlAtStartup: trueit has done so on every session start since you configured the proxy. The failed registration is the visible symptom; the leaked bearer is the actual incident. Containment, in order:claude→/login(or log out and back in). If your account settings expose active sessions / API credentials, revoke the old one there; if not, treat the exposed token as live until it expires and consider contacting support to invalidate it.remoteControlAtStartup: false(or unset it) and don't runclaude remote-control/--remote-controlwhile anHTTP_PROXY/HTTPS_PROXYis active. Every invocation behind the forward proxy re-sends the bearer in cleartext.CONNECT-tunnels correctly through the same proxy, so this is isolated to the Remote Control bridge client's proxy handling — not your proxy, not your env. That's exactly the right scoping for the maintainers.Why the operator can't fully fix it: the correct behavior (issue
CONNECT api.anthropic.com:443, then TLS to origin, so the bearer is never in cleartext) is in the bridge client and has to be fixed there — same as the relatedHTTPS_PROXYhttps://-scheme gap. Nothing on the operator side makes a forward-proxy + Remote Control combination safe today; the only safe operator posture is to keep Remote Control off the proxied path until the client honors the tunnel.One question that helps the maintainers triage severity: was the proxy on a trusted local host (e.g.
127.0.0.1mitmproxy for your own capture), or a real network/corporate egress proxy? The blast radius of the cleartext bearer is very different between the two, and stating it sets the right priority.I'm running into a similar problem with
claude remote-controlbehind a forward proxy (Squid).Environment:
HTTPS_PROXY=http://proxy:3128/HTTP_PROXY=http://proxy:3128CONNECTtoapi.anthropic.com:443Observed behavior:
The bridge client sends requests with the proxy's own port appended to the target host, instead of issuing a proper
CONNECT api.anthropic.com:443first:Squid logs confirm the tunnel is accepted but the upstream connection never completes (since
api.anthropic.comobviously isn't listening on port 3128):Would be great to see this fixed — happy to provide more logs/config if useful.
Still reproducible on 2.1.246 and 2.1.247 (macOS, official build), and I can add the root cause plus a verified fix.
Root cause: the bridge client performs
/v1/environments/bridge(and the rest of the bridge API) through the bundled axios instance, and axios's built-in env-proxy support never CONNECT-tunnels. WhenHTTPS_PROXYis set, axios rewrites the request to{host: <proxy>, path: "https://api.anthropic.com/…"}and picks the transport from the proxy's protocol (http:), so the request leaves as absolute-form cleartext HTTP — a long-standing axios adapter limitation. The main API client uses a different HTTP stack that tunnels correctly, which is why everything else works over the same proxy and only the bridge path breaks.Captured from inside the process on 2.1.247 (hooking
http.request):Depending on which edge answers, the 400 body is either the plaintext-HTTP API error the OP quoted or this Cloudflare HTML page; in the HTML case the CLI shows only the bare
Registration: Failed with status 400with no detail, which makes it look like an account/permissions problem.Verified fix: I patched
http.requestso that calls whosepathis an absolutehttps://URL (only axios's proxy mode produces those) are reissued as real HTTPS requests through aCONNECTtunnel to the proxy. With that in place,claude remote-controlworks fully behindHTTPS_PROXY=http://127.0.0.1:20813on 2.1.247 — registration returns 200, polling, session spawn and heartbeats all function. So fixing the bridge client's axios config (proxy: falseplus a CONNECT-capable agent — anHttpsProxyAgentis already bundled for other subsystems) or routing these calls through the same stack as the main API client would resolve this.Worth repeating the OP's security point: until this is fixed, every registration attempt sends the OAuth bearer token as cleartext beyond the proxy, so affected users should rotate credentials after testing.