Claude Code behind a proxy: which safety nets stop running (2026)
A proxy changes where Claude Code's requests go. It also changes which of its own safety nets are still running. The credential variable is a header choice, the base URL alone does not move your billing, and a gateway context limit turns off auto compact recovery. Field log, September 2026.
Updated on September 15, 2026
On this page
Quick answer
I spent 15 September 2026 reading how Claude Code behaves once something sits between it and the model, after a week of watching a colleague's gateway rollout produce failures that all looked like network problems and none of which were.
A proxy changes where your requests go. That part is obvious and it is the part every guide covers. What surprised me is the second half: it also changes which of Claude Code's own safety nets are still running, and the ones that switch off do it silently.
Three things are worth the read even if you never run a gateway.
Your credential variable is a header choice, not a naming preference. ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY travel in different HTTP headers. Put the right secret in the wrong variable and you get a 401 that looks exactly like a revoked key.
Pointing Claude Code at a gateway does not move the bill. Setting the base URL alone routes traffic through the gateway while your personal claude.ai login stays the active credential, so your own limits and billing still apply.
A gateway with its own context ceiling disables auto compact's recovery path. Not throttles it. Disables it, because the gateway rewrites the error into words Claude Code does not recognise.
The last one is the one I would not have guessed, and it is the one with a genuinely awkward workaround.
The moment
A teammate moved onto the company gateway on Friday and spent the afternoon filing what he was sure were three separate bugs: a 401 on a key that worked in curl, sessions dying at a context length that used to be fine, and /fast insisting it could not reach the network while ordinary prompts answered instantly.
They are three symptoms of one thing. A proxy is not a transparent pipe here. It is a participant.
Everything below is read from Anthropic's enterprise network configuration reference, its gateway connection reference and its LLM gateway overview, all read on 15 September 2026. I have flagged what I checked and what I am repeating.
Finding 1: the credential variable is a header choice
This is the cheapest fix on the page and the one that costs people the most time.
Each variable sends your credential in a different header:
Scroll to see more
| You set | It travels in |
|---|---|
ANTHROPIC_AUTH_TOKEN | Authorization: Bearer |
ANTHROPIC_API_KEY | x-api-key |
apiKeyHelper | both |
A credential in the wrong variable arrives at the gateway in a header the gateway does not read, and the request fails with a 401. The key is fine. The gateway is fine. The envelope is wrong.
Anthropic's own advice when your gateway team did not say which kind it is: use ANTHROPIC_AUTH_TOKEN, then switch if you get a 401. The apiKeyHelper route sidesteps the question entirely, because it populates both headers.
There is a precedence asymmetry underneath this that I have not seen written down anywhere else. With ANTHROPIC_AUTH_TOKEN, the variable takes precedence over a saved login immediately. With ANTHROPIC_API_KEY, you are prompted once in interactive mode to approve the key before it takes over. Same intent, two different behaviours, and the second one does not fire at all in a non-interactive run.
Finding 2: pointing at the gateway does not move the bill
Here is the half-configured state nobody warns you about.
ANTHROPIC_BASE_URL is the variable that points Claude Code at the gateway. Set only that, with no gateway credential, and requests do route through the gateway. But a saved claude.ai login remains the active credential, so its usage limits and billing apply.
So it is entirely possible to run a "gateway rollout" where the traffic is genuinely flowing through your infrastructure, your audit log is filling up, and every developer is still burning their personal subscription quota. Everything looks right. The thing the gateway exists to centralise, the credential, never moved.
Only a gateway credential variable or an apiKeyHelper replaces the subscription. The tell is in /status: an Auth token or API key line means a gateway credential is active, whereas a Login method line naming a claude.ai account means it is not.
One consequence for gateway operators: if you are passing that subscription traffic on to Anthropic, your gateway has to forward the OAuth capability in the anthropic-beta header, or it breaks.
Finding 3: the gateway's context limit turns off auto compact recovery
I wrote about what auto compact costs and what it loses yesterday. This is the part that only shows up behind a proxy, and it is a genuine hole.
Many gateways enforce a context window smaller than the model's native one. When you exceed it, the gateway returns its own error in its own words, something like ContextWindowExceededError or a message about a prompt token count exceeding a limit.
Claude Code does not recognise that as a too-long error. So it does not compact and retry automatically. The recovery path that normally makes a long session survive its own length simply does not fire, and the session dies instead.
The documented prevention is to set CLAUDE_CODE_AUTO_COMPACT_WINDOW to the gateway's limit so Claude Code compacts before the gateway objects. And here is the awkward part, quoted from the reference: Claude Code clamps that value to at least 100,000 tokens and at most the model's context window.
So if your gateway enforces a ceiling below 100,000 tokens, you cannot match it. The variable will not go that low. Running /compact by hand remains the only recovery, on a schedule you have to keep in your own head.
Worth pairing with CLAUDE_CODE_MAX_OUTPUT_TOKENS set below the gateway model's output limit, for the same reason in the other direction.
A small thing I noticed while reading around this: the most-installed proxy project in the search results, raine/claude-code-proxy, sets CLAUDE_CODE_AUTO_COMPACT_WINDOW to a specific value in its own configuration examples. It does not say why. Now you know why.
Finding 4: four watchdogs, and a gateway switches one off
Claude Code runs four independent timers that abort a streaming response when it goes quiet, so a dead connection fails and retries instead of hanging forever.
Scroll to see more
| Timer | Fires when |
|---|---|
| First byte deadline | no response headers arrive at all |
| Event level watchdog | no response events parse |
| Byte level watchdog | no bytes arrive, including keep alive pings |
| Body idle timeout | no bytes for five minutes |
The first one is the interesting one. Per the reference, the first byte deadline runs on the direct Anthropic API and on Claude Platform on AWS, including through an HTTPS proxy, but not when ANTHROPIC_BASE_URL or ANTHROPIC_AWS_BASE_URL routes them through a gateway.
That distinction is precise and easy to miss. A corporate HTTPS proxy keeps the deadline. A gateway, which is what you configure with a base URL, removes it. Same physical hop count, different behaviour, because Claude Code stops recognising the endpoint as its own.
The default timeouts also shift: 180 seconds on the direct Anthropic API, 300 seconds everywhere else. So behind a gateway you wait roughly twice as long before anything gives up, with one fewer timer watching.
If you want them back, note two clamps. CLAUDE_STREAM_IDLE_TIMEOUT_MS raises any value below five minutes up to five minutes, so you cannot tighten it that way. And setting CLAUDE_ENABLE_BYTE_WATCHDOG to 0 also turns off the first byte deadline, which is not what the variable name suggests. Neither variable extends a watchdog to a connection type it does not already cover.
Finding 5: some of your traffic never goes through the gateway
If your egress policy is "only the gateway", expect blocked connections in your monitoring, because several requests are not gateway traffic by design.
The fast mode availability check still calls api.anthropic.com even when ANTHROPIC_BASE_URL points elsewhere. It does honour a configured HTTP proxy, so an allowlist entry there is the fix when a network block is the cause. But the same connectivity error also appears when the check presents a gateway-issued credential that Anthropic rejects, and allowlisting does nothing for that, because nothing was blocked. One error message, two unrelated causes. That was my teammate's third "bug".
The WebFetch domain safety check also still calls api.anthropic.com, on every provider, unless you set skipWebFetchPreflight to true in settings.
Telemetry is the one to read carefully. While the base URL points at the gateway, Claude Code sends telemetry events to Anthropic without your gateway credential, which is the correct behaviour: a credential is attached only when the request goes to the host that credential belongs to. Before v2.1.246 it could attach the gateway credential to Anthropic-bound telemetry and usage-metrics requests. If you are running a gateway on an older build, that is worth knowing about.
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 turns most of it off, with three caveats: it disables auto updates, so you need another update path; it suppresses the fast mode availability check, so /fast reports unavailable; and it does not cover the WebFetch preflight, which needs its own setting.
Finding 6: your shell exports may not reach your background agents
Background agents do not run inside the terminal that dispatched them. A per-user supervisor process hosts them, and it is one process shared by every terminal.
It inherits the environment of whichever shell starts it first. An OS-installed supervisor receives no shell environment at all.
So if you export your proxy, CA path or gateway variables only in your shell, they reach background agents when that shell happened to cold start the supervisor, and silently do not when a different shell did. Same machine, same config, different answer depending on terminal history. That is about as reproducible as a coin toss.
The fix is to put the variables in the env block of ~/.claude/settings.json or managed settings instead.
{
"env": {
"ANTHROPIC_BASE_URL": "https://llm-gateway.example.com",
"ANTHROPIC_AUTH_TOKEN": "your-gateway-token",
"HTTPS_PROXY": "http://proxy.example.com:8080",
"NODE_EXTRA_CA_CERTS": "/etc/ssl/certs/corp-ca.pem"
}
}
Which surfaces a second rule worth memorising: when both a shell export and a settings file env block set the same variable, the settings file wins. That is the inverse of what most tooling does, and it means a stale settings file quietly overrides the export you are staring at.
One more, for anyone wrapping Claude Code in a corporate launcher: the supervisor starts Claude Code from a fixed path rather than looking up claude on PATH, so a wrapper placed earlier on PATH is bypassed by every background agent. That is what the processWrapper setting is for, and an already-running supervisor keeps the configuration it started with, so it needs claude daemon stop --any before the new setting takes.
Finding 7: /status has two rows that mean opposite things
This is my favourite detail on the page, because it is a trap built out of two reasonable decisions.
In /status, the mTLS client cert and mTLS client key rows appear only when the files actually loaded. A missing row means the load failed.
The Additional CA cert(s) row shows the NODE_EXTRA_CA_CERTS path without checking that the file loaded.
So in one panel, two adjacent rows: the presence of one is proof of success, and the presence of the other is proof of nothing at all. A visible CA path tells you a variable is set, not that a certificate was read.
Underneath this is a broader rule. Claude Code validates almost none of this at startup. The single exception is the proxy URL: if it cannot parse the value, for example one missing the scheme, it stops launch and names the variable. Everything else, wrong cert path, unreadable key, unreachable gateway, surfaces later as a connection error on some request.
The place to actually confirm is the debug log. Start with claude --debug and look for the lines that name each file loading. Note that the output goes to a file under ~/.claude/debug/, not to your terminal, which caught me out for a few minutes.
Finding 8: the small proxy rules that bite first
Four things that are pure fact and easy to get wrong.
Precedence is lowercase first. Claude Code uses the first one set in the order https_proxy, HTTPS_PROXY, http_proxy, HTTP_PROXY. If you have a stale lowercase variable in your profile, it beats the uppercase one you just exported.
SOCKS proxies are not supported. For proxies needing NTLM or Kerberos, the documented answer is to put a gateway in front instead.
Variables are read once at startup. A running session does not pick up later changes to your shell environment.
NO_PROXY accepts either separator, space or comma, and * bypasses everything. You do not need a loopback entry: Claude Code never sends WebSocket connections to localhost or the loopback range through the proxy.
There is also a nice failure mode worth recognising. If you see API returned an empty or malformed response (HTTP 200), that is usually a gateway or intermediate proxy answering with an HTML error or login page. A captive portal, returned with a success code.
What I did not verify
This is a documentation read. I do not have an LLM gateway in front of this machine, and I said the same thing when I wrote about fast mode two days ago, so I am not going to pretend otherwise now. I checked the /status rows, the debug log path and the proxy variable behaviour on a local install with a throwaway proxy. Everything about gateway credential precedence, context-limit error rewriting, watchdog coverage and telemetry credential attachment is repeated from Anthropic's references, not measured.
The version-gated claims carry their versions because the reference gives them: the telemetry credential change at v2.1.246, the Remote Control base URL block at v2.1.196, and the apiKeyHelper strictness about extra output at v2.1.227.
If you take one thing: after you put anything between Claude Code and the model, run /status and read it as a list of what is still switched on, not as confirmation that it worked.
Postscript: I have now spent longer reading about a proxy than my colleague spent configuring one, which is either thorough or a personality defect.
Written by
M. PatelM. Patel writes DevMoment field notes on AI dev workflow, tested on real work rather than demos.
Frequently asked questions
Why does my Claude Code gateway token return 401 when the same key works in curl?
Almost always the credential is in the wrong variable. ANTHROPIC_AUTH_TOKEN is sent in the Authorization Bearer header and ANTHROPIC_API_KEY is sent in the x-api-key header, so a token placed in the variable your gateway does not read arrives in a header it ignores and the request fails with a 401. An apiKeyHelper sends the value in both headers, which sidesteps the question. If you were not told which kind your gateway issues, Anthropic's reference suggests starting with ANTHROPIC_AUTH_TOKEN and switching on a 401. Read on 15 September 2026.
Does setting ANTHROPIC_BASE_URL move billing to my gateway instead of my Claude subscription?
No, not on its own. Anthropic's gateway reference states that setting only the base URL, without a gateway credential, routes requests through the gateway while a saved claude.ai login remains the active credential, so that subscription's usage limits and billing still apply. Only a gateway credential variable or an apiKeyHelper replaces the subscription. Check which one is active in /status: an Auth token or API key line means a gateway credential is in use, whereas a Login method line naming a claude.ai account means it is not. Read on 15 September 2026.
Why did Claude Code stop auto compacting after I moved to an LLM gateway?
If the gateway enforces a smaller context window than the model's native one, it returns its own error text rather than the upstream error. Claude Code does not recognise that wording as a too-long error, so it does not compact and retry automatically. Running /compact by hand recovers the session. To prevent it, set CLAUDE_CODE_AUTO_COMPACT_WINDOW to the gateway's limit, but note the documented clamp: the value is held to at least 100,000 tokens and at most the model's context window, so a gateway limit below 100,000 cannot be matched. Read on 15 September 2026.
Does Claude Code support SOCKS proxies?
No. Anthropic's enterprise network configuration reference states plainly that Claude Code does not support SOCKS proxies. It reads the standard HTTP proxy variables instead, using the first one set in the order https_proxy, HTTPS_PROXY, http_proxy, HTTP_PROXY, so a stale lowercase variable takes precedence over the uppercase one you just exported. For proxies requiring NTLM or Kerberos authentication the documented suggestion is to use an LLM gateway that supports your authentication method. Read on 15 September 2026.
Why does /fast report fast mode unavailable while ordinary Claude Code requests work?
The fast mode availability check calls api.anthropic.com directly rather than your gateway base URL, so it fails when that host is unreachable even though inference through the gateway is fine. The check does honour a configured HTTP proxy, so allowlisting api.anthropic.com in the proxy fixes the network-block case. The same connectivity error also appears when the check presents a gateway-issued credential that Anthropic rejects, and allowlisting does not help there because nothing was blocked. Read on 15 September 2026.
Why do my proxy environment variables not reach Claude Code background agents?
Background agents run under a per-user supervisor process that is shared by every terminal. It inherits the environment of whichever shell started it first, and an OS-installed supervisor receives no shell environment at all, so a shell export reaches background agents only when that shell happened to cold-start the supervisor. Put the variables in the env block of a settings file instead. Note also that when a shell export and a settings file env block set the same variable, the settings file value applies. Read on 15 September 2026.
Keep reading
Claude Code auto compact: what it costs and what you lose (2026)
Claude Code auto compact fires on a per-model token boundary, not a percentage. What it costs is set by prompt cache warmth rather than context size, and a path-scoped rule does not come back afterwards until Claude reads a matching file again.
Claude Code telemetry: what redaction does not cover (2026)
Claude Code's OpenTelemetry export redacts prompts and responses by default. It does not redact who you are. Four identity attributes ship on every datapoint with no switch to turn them off, and one environment variable quietly controls two streams.
Claude Code GitHub Actions: 11 app permissions, 3 used (2026)
Installing Claude Code GitHub Actions is two decisions, and the app install is the one that sticks. The Claude GitHub App grants 11 repository permissions, the action uses 3, you cannot accept a subset, and the set can grow later. A counted field log.