Как поделиться подпиской Claude: мой опыт с 9router
Дисклеймер: это описание моего опыта, а не рекомендация обходить ограничения. Совместное использование аккаунта и доступ через сторонние инструменты могут нарушать условия Anthropic и правила Claude Code. Прокси не отменяет эти правила и не защищает от блокировки. Повторять или нет — ваше решение и риск.
У меня была подписка Claude Max за $200 в месяц. Ею пользовались из Бразилии и России. Для работы с Claude Code из России использовали VPN: без него подключиться не получалось. Через некоторое время аккаунт заблокировали.
Подтверждённой причины блокировки у меня нет. Я не могу сказать, что дело было именно в VPN или совместном использовании аккаунта. Но эта история стала поводом разобраться, как устроить доступ к AI-инструментам через свой сервер.
Содержание
- Почему стал искать роутер
- Что делает 9router
- Как устроена моя схема
- Развёртывание 9router на сервере
- Open WebUI для веб-чата
- Шлюз в России и соединение RU → EU (необязательный шаг)
- Локальный вариант
- Что получилось
Почему стал искать роутер
На работе я уже видел отдельный прокси для Claude Code, Gemini и GPT. Мне был знаком подход: клиент обращается к одному входу, а подключения к моделям настраиваются за ним. Стал искать похожее решение для себя и нашёл 9router.
В 9router меня заинтересовали вход через OAuth и поддержка нескольких аккаунтов. Тогда мне хватало одного аккаунта Claude, но хотелось попробовать собрать подключения к разным AI-провайдерам в одном месте.
Что делает 9router
9router принимает запросы AI-клиентов, обращается к провайдерам моделей через настроенные подключения и возвращает ответы. В проекте есть совместимые API, преобразование форматов и управление подключениями. Способ авторизации зависит от конкретной интеграции: API-ключ или OAuth. Подробнее — в документации проекта.
Я использую 9router 0.5.99. В примерах ниже стоит образ decolua/9router:latest: я выбрал свежую версию. Если интерфейс или параметры отличаются от описанных, ответ стоит искать в репозитории на GitHub.
Как устроена моя схема
Я пробовал размещать сервисы в Бразилии и Европе. В схеме, которую описываю ниже, 9router находится на европейском сервере. К нему подключены подписка Claude (в 9router это провайдер Claude Code с моделями cc/...), Gemini и другие провайдеры.
В этой схеме у роутера и сети разные задачи. 9router выбирает подключение к модели и обрабатывает API-запрос. Путь от компьютера пользователя до роутера обеспечивает сеть. Gateway в России и WireGuard относятся именно к сетевой части.
На схемах ниже показан мой вариант с необязательным Gateway в России. Если прямой доступ к EU-серверу стабилен, можно обойтись без него.
Сценарий 1: работа в AI-клиенте
Для доступа из России сделал Gateway: входной сервер с отдельным доменом. Он принимает запросы и передаёт их по закрытой WireGuard-сети в Европу к 9router. Дальше роутер обращается к выбранному провайдеру.
AI-клиент
→ Gateway в России
→ закрытая сеть WireGuard
→ 9router в Европе
→ AI-провайдер
Схема маршрута клиента.
Клиенту нужны адрес шлюза и ключ самого 9router. Ключ провайдера хранится в роутере, а пароль панели нужен только для управления.
Настройки Claude Code
В ~/.claude/settings.json укажем адрес шлюза, ключ 9router и модели для разных режимов:
{
"env": {
"ANTHROPIC_BASE_URL": "https://router.example.com/v1",
"ANTHROPIC_API_KEY": "<КЛЮЧ_ВАШЕГО_9ROUTER>",
"CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "cc/claude-opus-5-5",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "cc/claude-sonnet-5-5",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "cc/claude-haiku-5-5",
"ANTHROPIC_DEFAULT_FABLE_MODEL": "cc/claude-fable-5-1"
},
"model": "cc/claude-opus-5-5"
}
Пример рассчитан на Claude Code 2.1.293 или новее: с этой версии клиент поддерживает Haiku 5.5. Перед подключением сверьте идентификаторы cc/... со списком моделей в своём 9router.
| Поле | Что задаём |
|---|---|
ANTHROPIC_BASE_URL |
Адрес шлюза с /v1, как в инструкции 9router. У меня клиент работает именно с таким адресом. |
ANTHROPIC_API_KEY |
Клиентский ключ 9router для заголовка x-api-key. Пароль панели сюда не подходит. |
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY |
При значении "1" Claude Code запрашивает /v1/models у шлюза и добавляет полученные модели в меню /model. |
ANTHROPIC_DEFAULT_OPUS_MODEL |
Какой ID использовать при выборе opus. |
ANTHROPIC_DEFAULT_SONNET_MODEL |
Какой ID использовать при выборе sonnet. |
ANTHROPIC_DEFAULT_HAIKU_MODEL |
Какой ID использовать для haiku и фоновых функций, которым нужен Haiku. |
ANTHROPIC_DEFAULT_FABLE_MODEL |
Какой ID использовать при выборе fable. |
model |
Начальную модель сессии. Здесь тот же Opus, что в ANTHROPIC_DEFAULT_OPUS_MODEL. |
Назначения алиасов описаны в настройках моделей Claude Code, обнаружение моделей — в инструкции для gateway.
Opus 5.5, Sonnet 5.5, Haiku 5.5 и Fable 5.1 Claude Code запускает с контекстом в миллион токенов без дополнительных настроек. Если через шлюз окно контекста оказалось меньше, можно добавить к ID суффикс [1m], например cc/claude-opus-5-5[1m]. Реальный лимит шлюза и провайдера всё равно нужно учитывать.
Этот же пример файлом: settings.example.json.
Сценарий 2: чат в браузере
Для веб-чата у меня есть Open WebUI в Европе. Второй домен Gateway в России передаёт запросы браузера по WireGuard к Open WebUI. Сам Open WebUI обращается к 9router внутри Docker-сети.
Браузер
→ второй домен Gateway в России
→ закрытая сеть WireGuard
→ Open WebUI в Европе
→ 9router по Docker-сети
→ AI-провайдер
Схема веб-чата.
В первом сценарии пользователь настраивает AI-клиент. Во втором он открывает веб-интерфейс, а подключение к моделям уже настроено в Open WebUI.
Развёртывание 9router на сервере
Для примера возьмём Linux-сервер с Docker Compose, OpenSSL и пакетным менеджером apt. Перед 9router поставим Caddy: он будет принимать HTTP и HTTPS. Из контейнеров наружу опубликуем порты только у Caddy — TCP 80/443. DNS A-запись домена роутера направим на IP сервера. Сертификат Caddy получает автоматически от центра сертификации через ACME (документация).
Настроить firewall
Я использую UFW. Если firewall на сервере отключён и вы не планируете его включать, этот шаг можно пропустить. В SSH_PORT укажем свой SSH-порт, в примере это 22:
sudo apt-get update
sudo apt-get install -y ufw
SSH_PORT=22
sudo ufw allow "${SSH_PORT}/tcp"
sudo ufw enable
sudo ufw status
Правила для 80/443 здесь не нужны. Caddy работает в контейнере, а опубликованные порты контейнеров Docker открывает сам, обычные правила UFW их не фильтруют (документация Docker). Если у хостера есть отдельный сетевой firewall, откроем в нём TCP 80/443 для Caddy.
1. Подготовить каталог и .env
mkdir -m 700 ai-hub
cd ai-hub
Сохраним .env.example:
ROUTER_DOMAIN=router.example.com
ACME_EMAIL=admin@example.com
JWT_SECRET=REPLACE_WITH_RANDOM_HEX_64
INITIAL_PASSWORD=REPLACE_WITH_ROUTER_PASSWORD
API_KEY_SECRET=REPLACE_WITH_ANOTHER_RANDOM_HEX_64
MACHINE_ID_SALT=REPLACE_WITH_INDEPENDENT_RANDOM_HEX_64
Скопируем его в .env и закроем доступ к файлу:
cp -n .env.example .env
chmod 600 .env
Для JWT_SECRET, API_KEY_SECRET и MACHINE_ID_SALT сгенерируем по отдельному значению:
openssl rand -hex 32
Впишем их в .env вместе со своим доменом, почтой для ACME и паролем панели.
2. Создать Compose и Caddyfile
Сохраним первый Compose как compose.yml в текущем каталоге:
name: ai-hub
services:
router:
image: decolua/9router:latest
restart: unless-stopped
expose:
- "20128"
environment:
DATA_DIR: /app/data
PORT: "20128"
HOSTNAME: "0.0.0.0"
NODE_ENV: production
BASE_URL: https://${ROUTER_DOMAIN:?Set ROUTER_DOMAIN}
NEXT_PUBLIC_BASE_URL: https://${ROUTER_DOMAIN:?Set ROUTER_DOMAIN}
JWT_SECRET: ${JWT_SECRET:?Set JWT_SECRET}
INITIAL_PASSWORD: ${INITIAL_PASSWORD:?Set INITIAL_PASSWORD}
API_KEY_SECRET: ${API_KEY_SECRET:?Set API_KEY_SECRET}
MACHINE_ID_SALT: ${MACHINE_ID_SALT:?Set MACHINE_ID_SALT}
AUTH_COOKIE_SECURE: "true"
REQUIRE_API_KEY: "true"
ENABLE_REQUEST_LOGS: "false"
volumes:
- router-data:/app/data
caddy:
image: caddy:2.11.7-alpine
restart: unless-stopped
depends_on:
- router
ports:
- "80:80"
- "443:443"
environment:
ROUTER_DOMAIN: ${ROUTER_DOMAIN:?Set ROUTER_DOMAIN}
ACME_EMAIL: ${ACME_EMAIL:?Set ACME_EMAIL}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
volumes:
router-data:
caddy-data:
caddy-config:
Рядом сохраним шаблон Caddy как Caddyfile:
{
email {$ACME_EMAIL}
}
{$ROUTER_DOMAIN} {
reverse_proxy router:20128 {
flush_interval -1
}
}
3. Запустить 9router
docker compose config --quiet
docker compose pull
docker compose up -d
После запуска откроем https://router.example.com/dashboard и войдём паролем INITIAL_PASSWORD. Обязательный вход в панель и требование API-ключа оставим включёнными.
Дальше подключим подписку. В панели откроем Providers → Connect Claude Code. 9router запустит вход через OAuth — авторизуемся в аккаунте Anthropic в браузере. После этого роутер сам обновляет токены и показывает расход пятичасовой и недельной квот. Второй и следующие аккаунты подключаются так же. Модели этого провайдера появятся в списке с префиксом cc/.
Затем создадим в панели клиентский ключ. Его вместе со своим HTTPS-доменом и моделью укажем в настройках Claude Code.
Open WebUI для веб-чата
4. Дополнить .env и Compose
В .env добавим переменные чата:
CHAT_DOMAIN=chat.example.com
WEBUI_SECRET_KEY=REPLACE_WITH_WEBUI_RANDOM_HEX_64
WEBUI_ADMIN_EMAIL=admin@example.com
WEBUI_ADMIN_PASSWORD=REPLACE_WITH_WEBUI_ADMIN_PASSWORD
ROUTER_API_KEY=REPLACE_WITH_KEY_CREATED_IN_9ROUTER
WEBUI_SECRET_KEY сгенерируем тем же openssl rand -hex 32. В панели 9router создадим отдельный ключ для Open WebUI и впишем его в ROUTER_API_KEY.
В services добавим Open WebUI:
open-webui:
image: ghcr.io/open-webui/open-webui:v0.12.0
restart: unless-stopped
depends_on:
- router
expose:
- "8080"
environment:
WEBUI_URL: https://${CHAT_DOMAIN:?Set CHAT_DOMAIN}
CORS_ALLOW_ORIGIN: https://${CHAT_DOMAIN:?Set CHAT_DOMAIN}
WEBUI_AUTH: "true"
ENABLE_SIGNUP: "false"
ENABLE_PERSISTENT_CONFIG: "false"
WEBUI_ADMIN_EMAIL: ${WEBUI_ADMIN_EMAIL:?Set WEBUI_ADMIN_EMAIL}
WEBUI_ADMIN_PASSWORD: ${WEBUI_ADMIN_PASSWORD:?Set WEBUI_ADMIN_PASSWORD}
WEBUI_SECRET_KEY: ${WEBUI_SECRET_KEY:?Set WEBUI_SECRET_KEY}
WEBUI_SESSION_COOKIE_SECURE: "true"
ENABLE_OLLAMA_API: "false"
ENABLE_OPENAI_API: "true"
OPENAI_API_BASE_URL: http://router:20128/v1
OPENAI_API_KEY: ${ROUTER_API_KEY:?Set ROUTER_API_KEY}
volumes:
- open-webui-data:/app/backend/data
В верхнеуровневый volumes добавим том для данных чата:
open-webui-data:
В services.caddy.environment добавим домен чата:
CHAT_DOMAIN: ${CHAT_DOMAIN:?Set CHAT_DOMAIN}
С ENABLE_PERSISTENT_CONFIG=false параметры подключения берутся из Compose.
5. Настроить домен чата и запустить
DNS A-запись домена чата тоже направим на IP сервера. В Caddyfile добавим отдельный блок:
{$CHAT_DOMAIN} {
reverse_proxy open-webui:8080 {
flush_interval -1
}
}
docker compose config --quiet
docker compose pull open-webui
docker compose up -d --force-recreate open-webui caddy
На пустой базе администратор создаётся из WEBUI_ADMIN_EMAIL и WEBUI_ADMIN_PASSWORD; регистрация закрыта. Теперь откроем https://chat.example.com, войдём и выберем текстовую модель.
Готовые файлы
Полный комплект ai-hub с 9router и Open WebUI: compose.yml, .env.example, Caddyfile. В каталоге ai-hub должно получиться три файла: compose.yml, .env и Caddyfile.
Шлюз в России и соединение RU → EU
Этот этап необязателен. Если у вас стабильно работает прямой доступ к EU-серверу, шаги 6–8 можно пропустить.
При подключении из России у меня периодически пропадает доступ к роутеру. Я подозреваю, что это связано с блокировками РКН, но доказательств у меня нет. Если у вас возникает похожая проблема, можно попробовать другой маршрут: через российский сервер с HAProxy и WireGuard. Гарантии доступности он не даёт.
На RU-сервере развернём HAProxy и соединим его с EU по WireGuard. Дальше Caddy на EU направит запросы в 9router или Open WebUI.
Для 9router и чата создадим DNS A-записи router-ru.example.com и chat-ru.example.com с публичным IP RU-сервера.
6. Соединить серверы WireGuard
На каждом сервере подготовим новый интерфейс wg-ai:
sudo apt-get update
sudo apt-get install -y wireguard
sudo -i
install -d -m 700 /etc/wireguard
cd /etc/wireguard
umask 077
# приватный ключ этого сервера
wg genkey > wg-ai.key
# публичный ключ из приватного
wg pubkey < wg-ai.key > wg-ai.pub
cat wg-ai.pub
Публичный ключ (wg-ai.pub) передадим на соседний сервер, приватный (wg-ai.key) оставим на месте.
RU — готовый файл:
[Interface]
Address = 10.77.0.1/32
PrivateKey = REPLACE_WITH_RU_PRIVATE_KEY
[Peer]
PublicKey = REPLACE_WITH_EU_PUBLIC_KEY
AllowedIPs = 10.77.0.2/32
Endpoint = 203.0.113.20:51820
PersistentKeepalive = 25
Этот конфиг сохраним на RU в /etc/wireguard/wg-ai.conf. Здесь 10.77.0.1 — адрес RU внутри туннеля.
EU — готовый файл:
[Interface]
Address = 10.77.0.2/32
ListenPort = 51820
PrivateKey = REPLACE_WITH_EU_PRIVATE_KEY
[Peer]
PublicKey = REPLACE_WITH_RU_PUBLIC_KEY
AllowedIPs = 10.77.0.1/32
Этот конфиг сохраним на EU в /etc/wireguard/wg-ai.conf. Здесь 10.77.0.2 — адрес EU внутри туннеля.
В PrivateKey вставим свой wg-ai.key, в PublicKey wg-ai.pub соседа. В RU Endpoint укажем публичный IPv4 EU.
Для UFW на EU добавим ещё одно правило: разрешим WireGuard с RU-сервера. В RU_PUBLIC_IP укажем публичный IP RU:
RU_PUBLIC_IP='REPLACE_WITH_RU_PUBLIC_IP'
sudo ufw allow from "$RU_PUBLIC_IP" to any port 51820 proto udp
Если у хостера есть сетевой firewall, на EU также разрешим в нём UDP 51820 от публичного IP RU.
На обоих серверах запустим туннель:
chmod 600 /etc/wireguard/wg-ai.conf
systemctl enable --now wg-quick@wg-ai
exit
В AllowedIPs только адрес соседа: default route не меняется, дополнительных правил NAT не требуется (WireGuard, wg-quick).
7. Добавить RU-домены на EU
В существующий .env добавим два домена RU:
RU_ROUTER_DOMAIN=router-ru.example.com
RU_CHAT_DOMAIN=chat-ru.example.com
В compose.yml, в services.caddy.environment, допишем:
RU_ROUTER_DOMAIN: ${RU_ROUTER_DOMAIN:?Set RU_ROUTER_DOMAIN}
RU_CHAT_DOMAIN: ${RU_CHAT_DOMAIN:?Set RU_CHAT_DOMAIN}
В services.open-webui.environment изменим две строки, чтобы чат работал по обоим доменам:
WEBUI_URL: ""
CORS_ALLOW_ORIGIN: https://${CHAT_DOMAIN:?Set CHAT_DOMAIN};https://${RU_CHAT_DOMAIN:?Set RU_CHAT_DOMAIN}
В существующем Caddyfile добавим RU-домены к блокам роутера и чата. Получится такой файл:
{
email {$ACME_EMAIL}
servers {
protocols h1 h2
}
}
{$ROUTER_DOMAIN}, {$RU_ROUTER_DOMAIN} {
reverse_proxy router:20128 {
flush_interval -1
}
}
{$CHAT_DOMAIN}, {$RU_CHAT_DOMAIN} {
reverse_proxy open-webui:8080 {
flush_interval -1
}
}
Из каталога ai-hub применим изменения:
chmod 600 .env
docker compose config --quiet
docker compose up -d --force-recreate caddy open-webui
В CORS_ALLOW_ORIGIN два HTTPS-адреса разделены ;. Старые EU-домены остаются в Caddy рядом с новыми RU-доменами. Блок servers { protocols h1 h2 } отключает HTTP/3: он работает поверх UDP, а HAProxy на RU пропускает только TCP.
Итоговые файлы для сверки: compose.yml, .env.example и Caddyfile.
8. Включить HAProxy на RU
На RU-шлюзе установим HAProxy. Для него понадобятся свободные TCP-порты 80/443:
sudo apt-get install -y haproxy
HAProxy здесь работает без Docker, поэтому UFW фильтрует его порты. Если firewall на RU включён, повторим команды UFW из начала статьи и откроем 80/443:
sudo apt-get install -y ufw
SSH_PORT=22
sudo ufw allow "${SSH_PORT}/tcp"
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
Конфигурацию сохраним в /etc/haproxy/haproxy.cfg:
global
log /dev/log local0
maxconn 4096
user haproxy
group haproxy
defaults
log global
mode tcp
timeout connect 10s
timeout client 4h
timeout server 4h
frontend http_in
bind :80
mode http
option httplog
acl allowed_host req.hdr(host),lower,field(1,:) -m str router-ru.example.com chat-ru.example.com
http-request deny deny_status 421 unless allowed_host
default_backend eu_http
backend eu_http
mode http
server eu 10.77.0.2:80 check
frontend https_in
bind :443
option tcplog
tcp-request inspect-delay 5s
acl client_hello req.ssl_hello_type 1
acl allowed_sni req.ssl_sni -i router-ru.example.com chat-ru.example.com
tcp-request content reject if client_hello !allowed_sni
tcp-request content accept if client_hello allowed_sni
tcp-request content reject if WAIT_END
default_backend eu_https
backend eu_https
server eu 10.77.0.2:443 check
В строках acl allowed_host и acl allowed_sni заменим router-ru.example.com и chat-ru.example.com на свои RU-домены, те же, что в RU_ROUTER_DOMAIN и RU_CHAT_DOMAIN на EU. Эти проверки задают, какие домены разрешены на шлюзе. Запросы будут уходить на 10.77.0.2 — приватный адрес EU.
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl enable haproxy
sudo systemctl restart haproxy
В Claude Code укажем ANTHROPIC_BASE_URL=https://router-ru.example.com/v1 и оставим прежний ключ 9router. Веб-чат открывается по https://chat-ru.example.com; экземпляр и данные на EU остаются теми же.
Скачать комплект RU/EU. Инструкции для RU и EU; DNS и порты — в общем README.
Локальный вариант
9router можно запустить локально, чтобы использовать несколько аккаунтов Claude или создавать комбо-модели. Для этого возьмём .env первого этапа и compose.local.yml:
docker compose -f compose.local.yml config --quiet
docker compose -f compose.local.yml pull
docker compose -f compose.local.yml up -d
Панель: http://localhost:20128/dashboard. Base URL для Claude Code: http://localhost:20128/v1. В панели можно подключить несколько разрешённых аккаунтов и настроить переключение между ними с учётом квот.
Что получилось
Теперь для работы из России и Бразилии у меня одна точка входа к моделям — 9router в Европе. Через него можно писать код в Claude Code и пользоваться чатом в Open WebUI. Сейчас одновременно работают 3–4 человека, и проблем не возникает.
Роутер и сеть живут отдельно. 9router отвечает за подключения к моделям, а Gateway в России и WireGuard только доставляют запросы до него. Поэтому новый провайдер или аккаунт добавляется в панели роутера, а сетевую часть при этом трогать не нужно.