Как запустить Claude Code из Китая, когда даже SOCKS5 «не работает»

Как запустить Claude Code из Китая, когда даже SOCKS5 «не работает»

▎ Просто дайте ссылку на эту статью Claude Code — и он вам сам всё подключит. Погнали.

Вводные

В последние годы начались послабления: если раньше ВКФ (Великий Китайский Файрвол) фильтровал по белому списку (всё, что явно не разрешено, — запрещено), то теперь времена изменились и фильтруется чёрный список (всё, что не запрещено, — разрешено).

Но сами Anthropic в Китае не работают. Совсем: api.anthropic.com, claude.ai, консоль — всё заблокировано. VPN решает вопрос, но каждый раз, когда отключаешь VPN, Claude Code падает с потерей авторизации — и это достало.

Предположим, у вас уже есть SOCKS5-прокси (я поднимал его под Telegram, и он прекрасно работал без всякого VPN). Логика была простая: раз SOCKS5 пробивает ВКФ для Telegram — направлю через него и Claude Code. Наивный я.

Дальше — честный рассказ о том, где я ошибся, как диагностировал, и почему в итоге пришлось дойти аж до VLESS+Reality.

Первая попытка: HTTP-прокси на VPS (и почему это тупик)

Первое, что приходит в голову: Claude Code читает переменные HTTPS_PROXY/HTTP_PROXY, значит поднимем на VPS обычный HTTP-прокси и укажем его. Поставил tinyproxy, повесил на порт 8888, прописал в ~/.claude/settings.json:

{ "env": { "HTTPS_PROXY": "http://user:pass@VPS_IP:8888", "HTTP_PROXY": "http://user:pass@VPS_IP:8888" } }

С VPN — работает. Без VPN — нет. «Ага, — подумал я, — Золотой Щит режет нестандартный порт 8888». Перенёс прокси на порт 80 (стандартный HTTP, его-то точно не трогают). Не помогло.

Вот тут начинается самое интересное, потому что гипотеза про порт была неверной.

Почему HTTP-прокси обречён, а SOCKS5 — нет

Я залез в лог tinyproxy на сервере и увидел показательную вещь: там не было ни одного моего входящего соединения. Пакеты из Китая просто не доходили до VPS. При этом сам сервер пинговался, а SOCKS5 на том же сервере работал.

Разница — в том, что Великий Файрвол видит в первом же пакете:

HTTP-прокси (метод CONNECT) обязан послать строку открытым текстом: CONNECT api.anthropic.com:443 HTTP/1.1 Имя api.anthropic.com летит в plaintext до всякого шифрования. GFW читает эту строку и обрывает соединение по имени хоста — отсюда и пустой лог tinyproxy: пакеты гибли на подлёте к серверу. Порт не важен, режется по содержимому.

SOCKS5 имя хоста не светит: оно уходит на сервер уже внутри зашифрованного TLS ClientHello, который начинается после SOCKS-рукопожатия. GFW видит только «какой-то TCP к какому-то IP» и пропускает.

Вот почему Telegram через SOCKS5 жил, а HTTP-прокси умирал на любом порту. Любой hostname-светящий прокси в Китае бесполезен для заблокированных доменов. Вывод: надо использовать SOCKS5.

Засада: Claude Code не умеет SOCKS5

Казалось бы — укажи SOCKS5 в HTTPS_PROXY и готово. Но нет. Официальная документация Anthropic прямо говорит:

▎ Claude Code does not support SOCKS proxies.

Под капотом Claude Code использует Node-библиотеку undici, и она:

  • игнорирует переменную ALL_PROXY (которую уважают многие другие инструменты);
  • понимает только http:///https:// в HTTPS_PROXY;
  • а если подсунуть HTTPS_PROXY=socks5://... — просто падает с ошибкой Invalid URL protocol: the URL must start with http: or https: (см. issue anthropics/claude-code #3387, закрыт как «not planned»).

Тупик? Нет. Решение — классический мост: локальный HTTP-прокси, который форвардит в SOCKS5.

Решение: локальный мост privoxy → SOCKS5

Спасибо автору статьи «Работаем с Claude Code на десктопе из России» — идея моста оттуда.

Идея: на своей машине поднять крошечный HTTP-прокси на 127.0.0.1, который заворачивает трафик в SOCKS5 на VPS. Тогда:

  • Claude Code шлёт CONNECT api.anthropic.com:443 на localhost — этот plaintext остаётся внутри вашего компьютера, GFW его не видит в принципе;
  • мост превращает запрос в SOCKS5-соединение, и имя хоста уходит на VPS уже спрятанным внутри TLS;
  • файрволу видно только зашифрованное SOCKS-соединение к вашему серверу.

Инструмент — privoxy. На macOS:

brew install privoxy

Открываем конфиг (/opt/homebrew/etc/privoxy/config на Apple Silicon, /usr/local/etc/privoxy/config на Intel) и добавляем форвард в наш SOCKS5. Строка listen-address 127.0.0.1:8118 там обычно уже есть по умолчанию:

listen-address 127.0.0.1:8118 forward-socks5t / user:pass@VPS_IP:1080 . socket-timeout 300

Важные детали:

  • forward-socks5t (с буквой t) — резолв DNS происходит на стороне SOCKS-сервера, а не локально. В Китае это критично: локальный DNS заблокированного домена может вернуть поддельный ответ, а так имя резолвится уже на VPS.
  • user:pass@VPS_IP:1080 — логин, пароль, адрес и порт вашего SOCKS5.
  • финальная . означает «дальше не форвардить».

Запускаем как сервис (Homebrew сам сделает автозапуск через LaunchAgent):

brew services start privoxy

Проверяем, что мост жив и реально пробивает блокировку без VPN:

→ должен вернуть IP вашего VPS, а не ваш китайский IP

Подключаем Claude Code

Теперь в ~/.claude/settings.json указываем Claude Code на локальный мост (не на VPS напрямую!):

{ "env": { "HTTPS_PROXY": "http://127.0.0.1:8118", "HTTP_PROXY": "http://127.0.0.1:8118", "NO_PROXY": "localhost,127.0.0.1,::1,*.local,host.docker.internal" } }

NO_PROXY нужен, чтобы локальные адреса (сам мост, MCP-серверы, Docker, dev-серверы) ходили напрямую, а не гонялись через VPS.

Приятный момент: Claude Code перечитывает settings.json динамически, на каждый промпт (поэтому слэш-команды и меняют настройки на лету). Полный перезапуск не нужен — следующий запрос уже пойдёт через мост.

Промежуточная цепочка:

Claude Code → privoxy (127.0.0.1:8118) → SOCKS5 (VPS:1080) → Anthropic

И тут терминальный Claude Code заработал без VPN. Можно было бы остановиться — но мне захотелось ещё и расширение Claude in Chrome. Вот здесь голого SOCKS5 и не хватило.

Диагностика: как понять, что именно блокируется

Самый коварный момент во всей истории — когда «не работает», но непонятно где. Я потерял кучу времени, проверяя только api.anthropic.com, хотя Claude Code ходит на несколько хостов. Чтобы такого не повторялось, держу под рукой скрипт, который прогоняет все хосты Claude Code тремя способами и сравнивает.

#!/usr/bin/env bash

Запускать дважды: с VPN (эталон) и без VPN. Сравнить построчно.

BRIDGE="http://127.0.0.1:8118" SOCKS="socks5h://user:pass@VPS_IP:1080" HOSTS=(api.anthropic.com claude.ai platform.claude.com downloads.claude.aistorage.googleapis.com bridge.claudeusercontent.com raw.githubusercontent.com)

probe() { curl "$@" -s -o /dev/null --max-time 12-w "%{http_code}/%{time_total}s" "https://1/"2>/dev/null∣∣echo"FAIL(1/"2>/dev/null∣∣echo"FAIL(?)"; }

printf "%-32s | %-8s | %-9s | %-9s\n" HOST DIRECT BRIDGE SOCKS5 for h in "HOSTS[@]";dod=HOSTS[@]";dod=(env -u HTTPS_PROXY -u HTTP_PROXY curl --noproxy '*' -s -o /dev/null--max-time 12 -w "%{http_code}/%{time_total}s" "https://h/"2>/dev/null∣∣echoFAIL)b=h/"2>/dev/null∣∣echoFAIL)b=(probe "$h" -x "BRIDGE")s=BRIDGE")s=(probe "$h" -x "$SOCKS") printf "%-32s | %-8s | %-9s | %-9s\n" "$h" "$d" "$b" "$s" done

Как читать результат (без VPN):

  • DIRECT отвечает (любой код 2xx–4xx) → хост не заблокирован, прокси для него не нужен.
  • DIRECT=FAIL, но BRIDGE/SOCKS5 отвечают → хост блокируется, мост спасает. Это и есть норма.
  • BRIDGE=FAIL, а SOCKS5 ok → проблема в privoxy/HTTP-слое, а не в SOCKS.
  • BRIDGE и SOCKS5 оба FAIL → GFW режет хост даже внутри SOCKS (по SNI). Тогда голого SOCKS5 мало — нужна обфускация.

Код HTTP 403/404/200/301 в выводе — это успех: значит TLS-соединение установилось и сервер ответил. А вот 000 или FAIL — обрыв/таймаут, то есть блокировка.

В моём случае скрипт показал, что через мост без VPN отвечают все нужные CLI-хосты (api.anthropic.com, claude.ai, platform.claude.com). А вот bridge.claudeusercontent.com упал и через мост, и через SOCKS5 — оба FAIL. Это тот самый случай «GFW режет по SNI даже внутри SOCKS». Хост этот — WebSocket-мост расширения Claude in Chrome; для терминала он не нужен, но если хочется плагин — нужна обфускация. Так я и пришёл к Reality.

Финальное решение: VLESS + Reality

Великий Файрвол умеет читать SNI — имя сайта в TLS ClientHello, которое в обычном TLS передаётся открыто. Голый SOCKS5 прячет имя от plaintext-инспекции, но SNI внутри TLS всё равно виден, и по нему GFW и зарезал bridge.claudeusercontent.com.

VLESS + Reality решает это радикально: трафик маскируется под настоящее TLS-соединение к реальному «безобидному» сайту (я взял www.163.com — крупный китайский портал, к нему ходить логично и подозрений ноль). SNI подделывается под этот сайт, рукопожатие неотличимо от настоящего, и GFW видит просто «человек открыл 163.com». Резать нечего.

Ставится через Xray-core. На VPS (официальный установщик XTLS):

Генерируем ключи Reality:

xray x25519 # privateKey + publicKey xray uuid # UUID клиента openssl rand -hex 8 # shortId

Конфиг сервера /usr/local/etc/xray/config.json — VLESS+Reality inbound на 443, маска www.163.com:

{ "inbounds": [{ "port": 443, "protocol": "vless", "settings": { "clients": [{ "id": "ВАШ-UUID", "flow": "xtls-rprx-vision" }], "decryption": "none" }, "streamSettings": { "network": "tcp", "security": "reality", "realitySettings": { "dest": "www.163.com:443", "serverNames": ["www.163.com"], "privateKey": "ВАШ-PRIVATE-KEY", "shortIds": ["ВАШ-SHORTID"] } } }], "outbounds": [{ "protocol": "freedom" }] }

systemctl enable --now xray

На своей машине ставим тот же Xray (brew install xray — но запускайте установку с пустым HTTPS_PROXY=, иначе brew зависнет, если у вас уже прописан прокси в окружении). Конфиг клиента /opt/homebrew/etc/xray/config.json поднимает локальный SOCKS5 на 127.0.0.1:10808 и через Reality идёт на VPS:

{ "inbounds": [{ "listen": "127.0.0.1", "port": 10808, "protocol": "socks", "settings": { "udp": true, "auth": "noauth" }, "sniffing": { "enabled": true, "destOverride": ["http", "tls"] } }], "outbounds": [{ "protocol": "vless", "settings": { "vnext": [{ "address": "VPS_IP", "port": 443, "users": [{ "id": "ВАШ-UUID", "encryption": "none", "flow": "xtls-rprx-vision" }] }] }, "streamSettings": { "network": "tcp", "security": "reality", "realitySettings": { "serverName": "www.163.com", "fingerprint": "chrome", "publicKey": "ВАШ-PUBLIC-KEY", "shortId": "ВАШ-SHORTID" } } }] }

brew services start xray

Теперь privoxy форвардим уже не в VPS-SOCKS5, а в локальный Xray (он сам шифрует и уводит в Reality):

forward-socks5t / 127.0.0.1:10808 .

Итоговая цепочка:

Claude Code → privoxy (8118) → Xray SOCKS5 (10808) → VLESS+Reality (VPS:443) → интернет

Проверяем bridge-хост, который раньше падал:

curl -x socks5h://127.0.0.1:10808 -o /dev/null -w "%{http_code}\n" https://bridge.claudeusercontent.com/

→ 426 (раньше был FAIL). Reality спрятал SNI — GFW ослеп.

Вайтлист: через прокси только AI, остальное напрямую

Гонять весь трафик через VPS глупо — китайские сайты будут тормозить, а ходить через Сингапур ради weibo.com незачем. У Xray есть встроенный роутинг: AI-домены — в туннель, всё остальное — direct (напрямую, с вашего реального IP). Добавляем в клиентский конфиг блок routing:

"routing": { "domainStrategy": "IPIfNonMatch", "rules": [ { "type": "field", "outboundTag": "reality-out", "domain": [ "domain:anthropic.com", "domain:claude.ai", "domain:claude.com", "domain:claudeusercontent.com", "domain:openai.com", "domain:chatgpt.com", "domain:gemini.google.com", "domain:googleapis.com" ] }, { "type": "field", "outboundTag": "direct", "network": "tcp,udp" } ] }

(и не забудьте добавить в outbounds теги: reality-out для VLESS и { "tag": "direct", "protocol": "freedom" }).

Проверка таймингами говорит сама за себя:

www.163.com: http=403 t=0.076s ← напрямую, мгновенно claude.ai: http=403 t=1.38s ← через туннель (Сингапур)

Китайский сайт за 76 мс, Claude — за 1.4 с. Ровно как надо.

Системный прокси: чтобы и Chrome заработал

Расширение Claude in Chrome ходит в сеть не через HTTPS_PROXY Claude Code, а по системным настройкам. macOS умеет SOCKS5 нативно (без пароля — потому что наш Xray слушает только 127.0.0.1, снаружи к нему не подключиться).

Системные настройки → Сеть → Wi-Fi → Подробнее… → Прокси → SOCKS-прокси:

  • Сервер: 127.0.0.1, порт: 10808, без логина/пароля.

Благодаря вайтлист-роутингу внутри Xray заворачивать весь Mac безопасно: AI пойдёт через туннель, остальное — напрямую, без замедления. После этого расширение Claude in Chrome подключается без VPN.

Бонус: то же самое под Windows

Логика та же, меняется обвязка (нет brew и LaunchAgent).

Privoxy. Скачайте Windows-инсталлятор с privoxy.org (https://www.privoxy.org/), установите. Конфиг — C:\Program Files (x86)\Privoxy\config.txt, добавьте те же listen-address и forward-socks5t. Поставьте службой: privoxy.exe --install, затем в services.msc задайте учётку и автозапуск.

Xray. Скачайте Xray-windows-64.zip с https://github.com/XTLS/Xray-core/releases, положите тот же клиентский config.json рядом, запускайте xray.exe run (для автозапуска оберните в nssm (https://nssm.cc/) как службу).

Claude Code. Настройки в %USERPROFILE%.claude\settings.json — идентично macOS.

Системный прокси. Параметры → Сеть и Интернет → Прокси → «Использовать прокси-сервер» вручную → SOCKS5 127.0.0.1:10808.

Заключение

Главный урок этой истории: проблема почти никогда не там, где кажется. Я был уверен, что дело в порте, потом — что в claude.ai, потом — что в перезапуске. А реальных причин было две, и обе про видимость имени хоста файрволу: HTTP-прокси светит его открытым текстом, а голый SOCKS5 прячет всё, кроме SNI. Лечится это тем, что прячет и SNI, — Reality.

Рабочая формула для Claude Code (и Chrome-плагина) из Китая:

  1. VLESS + Reality на VPS за пределами Китая — маскировка под реальный сайт, GFW не видит ни имени, ни SNI.
  2. Xray-клиент локально — поднимает SOCKS5 на 127.0.0.1:10808 с вайтлист-роутингом (AI → туннель, остальное → напрямую).
  3. privoxy (127.0.0.1:8118) поверх — потому что Claude Code не умеет SOCKS.
  4. Системный SOCKS5-прокси на 10808 — чтобы заработали Chrome и расширение.
  5. Диагностический скрипт под рукой — чтобы не гадать, а измерять.

VPN выключен, китайские сайты летают напрямую, а Claude Code и Chrome-плагин спокойно работают через незаметный для файрвола туннель. Хорошего вайбкодинга!