Куда на самом деле ходит Claude Code: ANTHROPIC_BASE_URL, два вида ключей и /status

от автора

Я долго думал, что Claude Code намертво привязан к аккаунту claude.ai. Оказалось, нет: это обычный клиент к HTTP-эндпоинту, и куда он ходит — настраивается двумя переменными. Понадобилось мне это по скучной причине: на работе появился внутренний LLM-шлюз, и весь трафик к моделям должен идти через него. Пока разбирался, наступил на все грабли, которые есть. Ниже — что именно нужно Claude Code от эндпоинта, как его подключить тремя способами, как проверить до запуска и что означают ошибки, которые вы обязательно увидите.

Сервисы и роутеры называть не буду — принципиально. Всё ниже одинаково работает с корпоративным шлюзом, вашим собственным прокси и любым сторонним эндпоинтом, лишь бы он говорил на нужном протоколе.

Что Claude Code просит от эндпоинта

Claude Code разговаривает по Anthropic Messages API. Ему нужен HTTPS-адрес, на котором отвечает POST /v1/messages, и ключ. Всё. Если эндпоинт отдаёт только OpenAI-совместимый /v1/chat/completions — с Claude Code он напрямую не заработает, это другой протокол. Уточните у владельца шлюза, какой формат у него есть, прежде чем что-то настраивать.

Второй вопрос, который надо задать владельцу: в каком заголовке он ждёт ключ. Вариантов два, и от ответа зависит, какую переменную вы будете ставить:

  • Authorization: Bearer <ключ> — тогда нужна переменная ANTHROPIC_AUTH_TOKEN

  • x-api-key: <ключ> — тогда ANTHROPIC_API_KEY

Если не сказали — ставьте ANTHROPIC_AUTH_TOKEN, а проверка ниже покажет, угадали ли вы. Половина всех 401 в этой теме — ключ положили в переменную, которая отправляет его не в тот заголовок.

Способ 1. Переменные в shell

Самый быстрый, годится чтобы попробовать:

export ANTHROPIC_BASE_URL=https://llm-gateway.example.comexport ANTHROPIC_AUTH_TOKEN=sk-gateway-keyclaude

Минус — живёт только в этом терминале. Откроете новую вкладку, и Claude Code снова пойдёт в claude.ai.

Способ 2. settings.json

Постоянный вариант. Файл ~/.claude/settings.json (на Windows — %USERPROFILE%\.claude\settings.json), блок env:

{  "env": {    "ANTHROPIC_BASE_URL": "https://llm-gateway.example.com",    "ANTHROPIC_AUTH_TOKEN": "sk-gateway-key"  }}

Два момента, о которых не пишут крупным шрифтом. Первый: если одна и та же переменная задана и в shell, и в settings.json, побеждает settings.json. Я час искал, почему export не действует, — вот поэтому. Второй: не кладите ключ в .claude/settings.json внутри проекта. Этот файл коммитится и уезжает всем, кто клонирует репозиторий. Для проекта есть .claude/settings.local.json, он в .gitignore по умолчанию.

Способ 3. apiKeyHelper

Если ключ ротируется или лежит в хранилище секретов, вместо статической переменной можно указать команду, которая печатает ключ в stdout:

{  "apiKeyHelper": "~/bin/get-gateway-key.sh"}

Команда должна печатать только ключ, без баннеров и логов, иначе Claude Code возьмёт мусор и вы получите загадочное «Your apiKeyHelper script is failing». Ключ из хелпера отправляется сразу в обоих заголовках, так что вопрос «bearer или x-api-key» здесь отпадает.

Проверьте эндпоинт до запуска Claude Code

Это главный совет статьи. Не запускайте claude, пока не убедитесь curl-ом, что эндпоинт живой и ключ подходит:

curl -X POST "$ANTHROPIC_BASE_URL/v1/messages" \  -H "Authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \  -H "anthropic-version: 2023-06-01" \  -H "content-type: application/json" \  -d '{"model":"claude-sonnet-4-6","max_tokens":32,"messages":[{"role":"user","content":"ping"}]}'

Если шлюз ждёт x-api-key, замените заголовок Authorization на x-api-key: $ANTHROPIC_API_KEY.

Как читать ответ:

  • Пришёл JSON, который начинается с {"id":"msg_ и содержит "content":[...] — всё работает.

  • Пришла ошибка про неизвестную модель — тоже хорошо: адрес и ключ верные, шлюз вас авторизовал и только потом отказал в модели. Значит, надо узнать, как модели называются именно на этом шлюзе (см. ниже).

  • 401 — ключ не тот или не в том заголовке. Попробуйте второй заголовок, прежде чем писать владельцу.

  • 403 с HTML-телом вроде «403 Forbidden», а в логах шлюза запроса вообще нет — вас режет что-то по дороге: CDN, корпоративный прокси, региональный фильтр. Это не проблема Claude Code.

Только после зелёного curl запускайте claude, отправьте любое сообщение и наберите /status. Во вкладке Status должны быть две строки: Base URL с вашим адресом и Auth token (или API key) с именем переменной. Если вместо этого там Login method с аккаунтом claude.ai — переменные до процесса не доехали.

Имена моделей

Claude Code внутри оперирует алиасами opus, sonnet, haiku, а фоновые задачи гоняет на haiku-классе. Если на вашем шлюзе модели называются иначе, чем в Anthropic, или каких-то нет, — пропишите соответствие:

{  "env": {    "ANTHROPIC_BASE_URL": "https://llm-gateway.example.com",    "ANTHROPIC_AUTH_TOKEN": "sk-gateway-key",    "ANTHROPIC_DEFAULT_SONNET_MODEL": "имя-sonnet-на-шлюзе",    "ANTHROPIC_DEFAULT_OPUS_MODEL": "имя-opus-на-шлюзе",    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "имя-haiku-на-шлюзе"  }}

Особенно важна строка про haiku: Claude Code дёргает эту модель для служебных вещей, и если её на шлюзе нет, вы получите 404 в самый неожиданный момент, хотя основная модель работает. Переменная ANTHROPIC_MODEL задаёт модель по умолчанию для новых сессий, а флаг --model перебивает всё на одну сессию.

Что вы теряете, уходя со шлюза claude.ai

Честно перечислю, потому что об этом узнаёшь постфактум:

  • Подписка claude.ai не используется, пока задана переменная с ключом. Трафик тарифицируется по токенам тому, чей ключ, и лимиты подписки к нему не относятся.

  • Remote Control и голосовой ввод недоступны — они завязаны на аккаунт claude.ai.

  • Всё, что шлюз не умеет пробрасывать (новые beta-заголовки, thinking, кэш), у вас работать не будет. Claude Code обновляется часто; шлюз должен успевать.

Типовые ошибки, коротко

  • Предупреждение при старте про два источника авторизации, «auth may not work as expected». Одновременно активны ключ из переменной и сохранённый логин claude.ai. Либо уберите переменную, либо /logout, чтобы остался только ключ.

  • 401 invalid token. Ключ не выпущен этим шлюзом или отправлен не в том заголовке. Сверьтесь с таблицей выше.

  • Claude Code просит логин, хотя curl проходит. Переменные не унаследовались процессом: терминал новый, IDE стартовала не из этого shell. Перенесите их в settings.json.

  • 400 с упоминанием thinking или adaptive. Шлюз не понимает новые параметры Claude Code. Это к владельцу шлюза.

  • Таймауты на длинных ответах. По умолчанию запрос ждёт 10 минут; API_TIMEOUT_MS в миллисекундах, если надо больше. Если через прокси — проверьте, что сам прокси не режет соединение раньше.

Два слова про прокси

HTTPS_PROXY и ANTHROPIC_BASE_URL — про разное. Первое говорит, через что ходить наружу, второе — куда. Их можно сочетать: базовый URL на шлюз, а прокси — потому что до шлюза иначе не достучаться. NO_PROXY через запятую исключает хосты. Если Claude Code «не работает через VPN», в девяти случаях из десяти проблема в том, что прокси задан для shell, но не для процесса, из которого стартует IDE.

Вместо вывода

Claude Code — обычный клиент, и его можно направить куда угодно, лишь бы там был Messages API. Но «можно направить» не значит «стоит доверять»: по ответу шлюза можно проверить как минимум две вещи — что вернулось в поле model и появляется ли cache_read_input_tokens в usage на повторных запросах. Как это делать системно, по списку эндпоинтов, — отдельная история, соберу в следующий раз.

ссылка на оригинал статьи https://habr.com/ru/articles/1080716/