Claude Code LLM Gateway: Routing, Credentials, and Headers
Many companies already run an LLM gateway, such as LiteLLM, a cloud API gateway, or an in-house proxy, between their developers and the model providers. Claude Code works behind one: point it at the gateway's URL, give it a gateway-issued credential, and every model request goes through the gateway instead of straight to Anthropic or your cloud provider.
The setup is two environment variables. The hard part is everything around them: which credential variable puts the key in which header, how to distribute the configuration so developers do not set it by hand, which headers and body fields the gateway must pass through untouched, and what still bypasses the gateway. This guide follows Anthropic's gateway documentation for each of those, and ends with the errors you are most likely to hit.
Anthropic also ships its own self-hosted gateway, the Claude apps gateway, aimed at organisations that run Claude through Bedrock, Google Cloud, or Foundry. It is covered in Claude Apps Gateway. For the managed settings used to distribute gateway configuration, see Claude Code Enterprise Setup.
What a gateway gives you, and what it costs
A gateway gives an organisation one place to manage five things:
- Credentials: the provider key stays on the gateway. Developers hold a gateway credential, and offboarding someone revokes one key.
- Usage tracking: usage is attributed by developer or team, whichever provider served the request.
- Cost controls: budgets and rate limits are enforced in one place.
- Audit logging: every model request can be logged for compliance.
- Provider switching: change the upstream in the gateway's configuration, without touching laptops, as long as the gateway exposes one Anthropic-format endpoint whatever the upstream.
The cost is that the gateway becomes infrastructure you operate, and it has to keep up with Claude Code. New capabilities arrive as new anthropic-beta header values and new request fields. A gateway that strips or rejects them breaks the matching features, sometimes with an error and sometimes silently.
Billing changes too. While a gateway credential (or an apiKeyHelper) is active, requests carry that credential instead of a developer's claude.ai login, so subscription usage limits do not apply and the traffic is billed per token to whoever owns the key the gateway forwards, such as your Console organisation or your Bedrock account. Setting only ANTHROPIC_BASE_URL without a gateway credential routes traffic through the gateway but still bills the developer's subscription.
Quick reference
- Anthropic does not endorse, maintain, or audit third-party gateway products, and does not support routing Claude Code to non-Claude models through a gateway.
- Sessions that go through a gateway are not eligible for the HIPAA configuration.
- A custom
ANTHROPIC_BASE_URLalso makes Claude Code skip fetching server-managed settings, so deliver policy for gateway users through MDM or a managed settings file.
Remember this
A gateway centralises keys, budgets, and logs, at the price of a compatibility surface you must maintain on every Claude Code release.
Connect one machine: base URL, credential, verify
Every connection needs two variables: the gateway's base URL and a credential. Which credential variable you use decides which HTTP header carries it:
ANTHROPIC_AUTH_TOKENis sent asAuthorization: Bearer .... Use it when the gateway team says "bearer token" or "Authorization header".ANTHROPIC_API_KEYis sent asx-api-key: .... Use it when they say "API key" or "x-api-key".apiKeyHelperis a command whose output is sent in both headers. Use it when the credential rotates or comes from a vault.
A credential in the wrong variable reaches the gateway in a header it does not read, and you get 401.
Test with shell exports first, before Claude Code is involved:
1export ANTHROPIC_BASE_URL=https://llm-gateway.example.com2export ANTHROPIC_AUTH_TOKEN=sk-gateway-key3 4curl -sS -w '\n%{http_code}\n' -X POST "$ANTHROPIC_BASE_URL/v1/messages" \5 -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \6 -H "anthropic-version: 2023-06-01" \7 -H "content-type: application/json" \8 -d '{"model": "claude-sonnet-4-6", "max_tokens": 1, "messages": [{"role": "user", "content": "."}]}'A JSON body starting with {"id":"msg_ means the URL and credential work. An "unknown model" error also proves the credential works, since the gateway authenticated the request before rejecting the model. A 401 means switch to the other credential variable.
Then start claude from the same shell and run /status. The Anthropic base URL row should show the gateway, and an Auth token or API key row should name your variable. If you see a Login method row with a claude.ai account instead, the credential did not reach the session.
Quick reference
- To persist the values, put them in the
envblock of~/.claude/settings.json. A settings-file value wins over a shell export of the same variable. - Never put the credential in a project's
.claude/settings.json: that file is committed and shared with everyone who clones the repository. ANTHROPIC_AUTH_TOKENtakes over from a saved login immediately;ANTHROPIC_API_KEYasks for a one-time approval in interactive mode first.- Shell exports do not reliably reach background agents or an editor launched from the dock. Use a settings file for anything that must always route through the gateway.
Remember this
Pick the credential variable by the header your gateway reads, prove it with curl, and confirm with /status.
Roll it out: per-developer keys and managed settings
For an organisation, the sequence is the same whichever gateway product you run:
1. Deploy the gateway and give it the provider credential.
2. Issue each developer their own gateway credential, so usage is attributed and offboarding revokes one key.
3. Distribute the base URL through a managed settings file and the credential through your secrets tooling, so developers configure nothing.
4. Have each developer check /status before they depend on it.
For rotating credentials, an apiKeyHelper is a command that prints the current key. Claude Code caches its output for five minutes by default (CLAUDE_CODE_API_KEY_HELPER_TTL_MS changes that) and sends it in both credential headers:
1{2 "apiKeyHelper": "~/bin/get-gateway-key.sh",3 "env": {4 "ANTHROPIC_BASE_URL": "https://llm-gateway.example.com",5 "CLAUDE_CODE_API_KEY_HELPER_TTL_MS": "900000"6 }7}The helper must print nothing but the key; since v2.1.227 a banner or log line alongside it makes the helper fail.
To make the gateway the only allowed destination on managed machines, set allowedProviders to ["customEndpoint"] in the managed settings file and put the gateway's ANTHROPIC_BASE_URL in that file's env block (Claude Code v2.1.285 or later). Claude Code then refuses sessions pointed anywhere else, including Anthropic directly or a developer's own proxy.
Quick reference
- VS Code: set the variables in
claudeCode.environmentVariablesin VS Code's user settings; values only in~/.claude/settings.jsondo not reach the extension's own login check. - Desktop app: gateway routing comes from its third-party inference configuration, not from
ANTHROPIC_BASE_URL. An admin can distribute it, or a user can set it under Developer mode. - GitHub Actions: put
ANTHROPIC_BASE_URLin the workflowenv; the action passesanthropic_api_keyasx-api-key. For a bearer-token gateway, also setANTHROPIC_AUTH_TOKENinenv. - Cloud sessions on the web and Claude in Slack are not part of a gateway deployment, and Remote Control is unavailable while a gateway credential is active. If traffic must stay on the gateway, do not enable those surfaces.
Remember this
One credential per developer, configuration through managed settings, and allowedProviders if the gateway must be the only route.
What the gateway must pass through
An Anthropic-format gateway serves POST /v1/messages (Claude Code sends /v1/messages?beta=true, so match on the path) and optionally /v1/messages/count_tokens. Without token counting, Claude Code falls back to a character-based estimate of context use.
The rules from Anthropic's compatibility guide:
- Forward
anthropic-versionandanthropic-betaunchanged. Do not allowlist individual beta values; the set changes with every release. - Forward request body fields unchanged, as an open list. Many capabilities are a pair: a beta header plus a body field. Strip one half and you get a hard
400; strip both and the feature turns off quietly. - Keep
cache_controlmarkers wherever they appear, and do not flatten block-formsystemcontent into a string. Breaking this produces no error; every turn is just billed as uncached input. - Stream without buffering. Claude Code reads server-sent events as they arrive and expects the full sequence through
message_stop. A gateway that buffers stalls the client, and one that drops SSEpingevents can trip the idle timeout during long thinking pauses. - Forward error bodies unmodified. Claude Code's automatic recovery, such as retrying without a rejected capability, matches on the upstream's error wording.
Headers you may consume rather than forward include the credential headers and x-claude-code-session-id, which identifies all requests from one session. On requests from subagents, x-claude-code-agent-id identifies the agent, which helps attribute the cost of parallel agents.
Quick reference
- If the gateway translates Anthropic-format requests to Bedrock or Google Cloud, it must bridge the schema difference. The simpler option is to expose that provider's own format and have clients use
ANTHROPIC_BEDROCK_BASE_URLorANTHROPIC_VERTEX_BASE_URLwith the matchingCLAUDE_CODE_USE_*and skip-auth variables. CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1stops most pre-release capabilities and their body fields when the upstream rejects them, at the cost of those features (MCP tool search among them).- Claude Code prepends an attribution block to the system prompt. Keep the
systemarray in order; if the gateway must reshape it, setCLAUDE_CODE_ATTRIBUTION_HEADER=0on the client instead of stripping the block.
Remember this
Forward Anthropic headers, body fields, cache markers, and SSE events untouched. Allowlists break on the next release.
The errors you will most likely hit
Most gateway problems show up in one of two places: at authentication, as 401 or an unexpected login screen, or on the first real request, as a 400 naming a field the upstream does not accept. The curl test from the connect section separates them. If curl works and Claude Code does not, the problem is in how Claude Code receives the configuration, or in fields that only real sessions send.
The WAF case is worth remembering because it is confusing. A short curl test passes, but real prompts contain XML-style tags and source code that match cross-site-scripting body rules, so a firewall in front of the gateway blocks them before the gateway logs anything.
| Symptom | Cause | Fix |
|---|---|---|
| 401 invalid token | Credential in a header the gateway does not read, or revoked | Switch between ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY; reissue the key |
| 400 naming context_management or Extra inputs are not permitted | Upstream rejects fields Claude Code sends to Anthropic-format endpoints | Forward both halves, or set CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 |
| 403 HTML page, nothing in gateway logs | A WAF in front of the gateway blocks the body as cross-site scripting | Exempt /v1/messages from body inspection (AWS WAF: CrossSiteScripting_Body) |
| Stalls mid-response | Gateway buffers the stream or drops ping events | Relay SSE event by event, including pings |
| 400 with the gateway's own context-limit wording | Gateway enforces a smaller window; Claude Code does not recognise it | Run /compact; set CLAUDE_CODE_AUTO_COMPACT_WINDOW to the gateway's limit |
| TLS errors while curl works | Corporate TLS inspection; Node does not trust the CA | Set NODE_EXTRA_CA_CERTS to the CA bundle |
| Login screen although curl works | No credential Claude Code can read before first-run setup | Set the credential in a shell export, ~/.claude/settings.json, or managed settings |
Quick reference
- Start
claude --debugand read~/.claude/debug/<session-id>.txtfor the request that failed. /statusshows the active base URL and credential source; check it before changing gateway configuration.- Test every new Claude Code release against your gateway before it reaches the whole organisation.
Remember this
Use curl to tell gateway problems from client problems, and suspect a WAF when real sessions fail but curl works.
What still does not go through the gateway
A gateway carries model requests. It is not a full egress control for Claude Code:
- Background traffic such as version checks, telemetry, and release notes goes to Anthropic and to services like GitHub, outside the gateway path. Since v2.1.246, Claude Code does not attach the gateway credential to that traffic.
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1turns most of it off, but it also disables auto-updates, so plan another update path. - The fast-mode availability check and the WebFetch domain safety check call
api.anthropic.comdirectly. On a network that blocks it,/fastreports a connectivity error while inference keeps working.skipWebFetchPreflight: trueturns the WebFetch check off. - Tools Claude runs are not model requests at all. A
curlornpm installin the Bash tool uses the machine's normal network. Use the sandbox's network allowlist for that.
And the gateway only controls what reaches it. A developer who unsets the variables talks to Anthropic directly, unless managed settings pin allowedProviders and the network blocks other routes.
Quick reference
- If egress must be gateway-only, combine
allowedProviders: ["customEndpoint"],CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1, the sandbox network allowlist, and firewall rules. - Gateway credentials are long-lived secrets on laptops. Prefer an
apiKeyHelperthat fetches short-lived keys from your vault or SSO. - Slack and web cloud sessions run on Anthropic infrastructure and ignore gateway variables; disable them for users whose traffic must stay on the gateway.
Remember this
The gateway covers model calls. Background traffic, a few direct checks, and the commands Claude runs need their own controls.
Hands-on: route a session through a gateway
Use a gateway you control, or a local LiteLLM proxy pointed at your own API key.
1. Export ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN, run the curl test, and confirm a msg_ response.
2. Swap the token into ANTHROPIC_API_KEY instead and rerun curl with the Authorization header. Note the 401; this is the most common misconfiguration.
3. Start claude, run /status, and confirm the base URL and credential rows.
4. Ask Claude a question that reads a few files, then check the gateway logs for the x-claude-code-session-id header and the anthropic-beta values it received.
5. Move the two variables into ~/.claude/settings.json, open a new terminal without the exports, and confirm /status still shows the gateway.
Pass criterion: the curl test and a real session both succeed, you can find one session's requests in the gateway logs by session ID, and you can say which header your gateway reads the credential from.
Quick reference
- If step 4 shows the gateway rewriting or dropping
anthropic-beta, fix that before any wider rollout. - If the session stalls on long answers, check whether the gateway buffers streaming responses.
- Write down the gateway and Claude Code versions you tested; retest when either changes.
Remember this
Prove the header mapping, the session ID in logs, and persistent config on one machine before rolling out.
Key takeaway
Running Claude Code through an LLM gateway takes two variables, ANTHROPIC_BASE_URL and a credential, but the credential variable decides the header (ANTHROPIC_AUTH_TOKEN for Authorization: Bearer, ANTHROPIC_API_KEY for x-api-key, apiKeyHelper for both). Roll it out with one key per developer, the base URL in managed settings, and allowedProviders if the gateway must be the only route.
On the gateway side, forward anthropic-beta, anthropic-version, body fields, cache_control, and SSE events unchanged, and forward error bodies as they are. Then remember the gateway's limits: background traffic, a few direct checks, and the commands Claude runs do not pass through it. For Anthropic's own gateway with SSO sign-in and per-group policy, see Claude Apps Gateway; for the sandbox that limits what commands can reach, see Claude Code Sandboxing and Isolation.
Practice (40 min): complete the five steps above, then write the managed settings file you would distribute, with the base URL, apiKeyHelper, and allowedProviders.
References
- Claude Code docs: Other LLM gateways — checked Oct 17, 2026
- Claude Code docs: Connect Claude Code to an LLM gateway — checked Oct 17, 2026
- Claude Code docs: Gateway compatibility guide — checked Oct 17, 2026
Shoaib Hossain
Lead Author & Systems ArchitectSoftware engineer and distributed systems architect specializing in backend scalability, cloud-native infrastructure, databases, and AI engineering workflows. Author and maintainer of Core Concept Learning.
Related Articles
Explore this topic