Как я заставил бесплатные LLM работать из России и что узнал про Cloudflare

Недавно я потратил вечер на задачу, которая звучит просто: «хочу, чтобы бесплатные модели отвечали из России». Оказалось, что там три разные проблемы, которые легко перепутать, и одна ловушка, в которую я сам и попал.
Рассказываю по порядку, с командами и логами.
Проблема первая: 403 Access denied by security policy
Первое, что я увидел, запустив агент с бесплатными моделями:
AI_APICallError: Forbidden
Никаких деталей. Я решил, что дело в ключе — сгенерировал новый, вставил. Тот же результат.
Тогда я посмотрел на сырые заголовки ответа:
HTTP 403 Forbidden
Server: cloudflare
CF-RAY: a4d9c891184604fe-HKG
Set-Cookie: __cf_bm=...
{"success": false, "error": "Access denied by security policy."}
Вот оно. CF-RAY — значит, ответил Cloudflare, а не сам сервис. Access denied by security policy — это блокировка на входе, по репутации IP. Запрос не дошёл до OpenRouter вообще: ключ не проверялся.
Проверить это просто:
403 + Access denied by security policy → блок по IP
401 + Authentication Fails → ключ неверный или не от того сервиса
404 + Not found for account → модель недоступна аккаунту
429 → лимит исчерпан
Смена ключа при 403 не помогает. Я проверил два разных ключа с одного IP — оба дали одинаковый 403.
Дальше интереснее. Я запускал проверку несколько раз и заметил, что меняется CF-RAY:
CF-RAY: ...-HKG (Гонконг)
CF-RAY: ...-DME (Москва)
Блок не привязан к конкретному датацентру. Он привязан к моему IP. Значит, лечится только сменой адреса.
Проблема вторая: WARP не работает, и это неочевидно
Первый совет, который дают в такой ситуации — «поставь Cloudflare WARP, он бесплатный».
У меня стоял клиент с готовым WARP-профилем. При попытке подключиться:
error patching outbound[m4 § 0][custom]:
netip.ParsePrefix("/24"): ParseAddr(""): unable to parse IP
Я полез в конфиг:
"warp": {
"enable": false,
"clean-ip": "auto",
"account-id": "",
"access-token": "",
"wireguard-config": ""
}
Пусто. WARP-аккаунт не зарегистрирован, поэтому clean-ip: auto не может ничего разрешить и подставляет пустую строку — парсер падает.
Почему не зарегистрирован? Проверяю эндпоинты:
188.114.97.170:894 → заблокирован
162.159.192.1:894 → заблокирован
api.cloudflareclient.com → timeout
Серверы регистрации WARP недоступны. Поэтому аккаунт не создаётся, поэтому профиль мёртвый.
И вот ирония, ради которой стоило всё это писать: WARP выдаёт вам адрес Cloudflare. А блокирует вас тоже Cloudflare. Даже если бы он подключился, результат непредсказуем — вы пришли бы к Cloudflare через Cloudflare.
Вывод: WARP в России для этой задачи не работает. Не «надо настроить», а именно не работает — эндпоинты режутся.
Что работает: локальный прокси вместо VPN
Полноценный VPN требует прав администратора — создание TUN-адаптера. У меня их не было, и это оказалось не проблемой.
Есть разница, которую многие путают:
VPN (TUN) | Локальный прокси | |
|---|---|---|
Заворачивает | весь компьютер | только указанные программы |
Нужен админ | да | нет |
Подходит для | браузер, игры, всё | конкретный инструмент |
Мне нужно было, чтобы работал один агент. Хватило локального прокси: sing-box слушает 127.0.0.1:18888, и нужная программа ходит через него.
Ловушка, в которую я попал
Я сделал то, что кажется логичным — прописал прокси глобально:
[Environment]::SetEnvironmentVariable('HTTPS_PROXY','http://127.0.0.1:18888','User')
Агент заработал. А потом у меня перестал запускаться Roblox:
HttpError: ConnectFail
Critical failure fetching flags, terminating application.
Он пытался идти через мой прокси, а прокси в тот момент перезапускался. Соединение рвалось, клиент падал.
Глобальные переменные прокси наследуют все программы. Игры, лаунчеры, обновляторы — всё, что не должно идти через прокси, идёт через него и ломается.
Правильно — задавать прокси только внутри процесса:
$env:HTTPS_PROXY = 'http://127.0.0.1:18888'
& myapp.exe
Тогда он виден одной программе и никому больше.
Проблема третья: где брать серверы
Публичные vless:// конфиги лежат в открытых репозиториях. Я скачал список:
2196 reality-конфигов, 1053 уникальных хоста
Дальше — самое интересное. Проверять их через ping или Test-NetConnection бесполезно. У Reality TCP-порт может быть открыт, а рукопожатие — не проходить. Порт открыт, сервер принимает соединение, но TLS-сессия не поднимается.
Правильная проверка — реальный HTTP-запрос через прокси, и сравнение выходного IP с вашим:
Invoke-RestMethod https://api.ipify.org -Proxy http://127.0.0.1:18888
Если вернулся ваш IP — трафик шёл напрямую, прокси не сработал.
Замер: из 5681 конфига живыми оказались 2.
И тут я поймал сам себя
Я написал скрипт проверки на Python. Запустил — он бодро отрапортовал:
[1] OK 77.110.105.122 exit IP 94.245.148.251
[2] OK 158.160.160.21 exit IP 94.245.148.251
...
working: 6 / 6
Шесть из шести! Отличный результат. Кроме одной детали: все шесть вернули один и тот же IP — мой собственный. То есть urllib молча шёл напрямую, а скрипт засчитывал это как успех.
Я добавил сравнение с собственным IP — и результат стал честным:
[1] fail 77.110.105.122
[2] fail 158.160.160.21
working: 0 / 8
Мораль: проверка, которая не отличает «сработало» от «не сработало, но запрос всё равно прошёл», хуже отсутствия проверки. Она даёт ложную уверенность.
Что делать с этим дальше
Схема, которая у меня в итоге заработала:
Скачать публичный список
vless://конфиговОтфильтровать по реальному запросу, а не по открытому порту
Поднять локальный прокси на выживших
Задавать переменные прокси внутри процесса, а не глобально
Пункты 2 и 4 — те самые грабли, на которые я наступил. Если делать это вручную, на шаге 2 легко получить ложный результат, а на шаге 4 — сломать себе полсистемы.
Поэтому оба шага я закрыл скриптами. Они лежат в открытом репозитории вместе с таблицей провайдеров (какие требуют карту, какие VPN, какие лимиты):
github.com/n1lokaDeep/free-llm-russia
Файл | Что делает |
|---|---|
| отбирает кандидатов из публичного списка конфигов |
| проверяет каждый реальным запросом (с защитой от ложных срабатываний) |
поднимает локальный прокси на | |
управление: start / stop / restart / status |
Порядок:
python scripts/check-configs.py --top 80
python scripts/verify-live.py
powershell -File scripts/start-proxy.ps1
powershell -File scripts/proxy.ps1 status
Короткие выводы
403 Access denied by security policy— это блок по IP, а не проблема с ключом. СмотритеCF-RAYв заголовках: если он есть, ответил Cloudflare, а не сервис.WARP в России не работает — серверы регистрации заблокированы. И даже если бы работал, вы пришли бы к Cloudflare через Cloudflare.
Прокси не прописывайте глобально. Это ломает игры и лаунчеры с
ConnectFail. Только внутри процесса.Проверяйте прокси реальным запросом и сравнением IP. Открытый TCP-порт ничего не значит. Скрипт, который этого не делает, покажет «6 из 6 работает», когда не работает ничего.
Публичные конфиги быстро умирают. Из 5681 живых было 2. Нужен автоматический отбор, а не ручной перебор.
Если вы наступали на похожие грабли — расскажите в комментариях, интересно сравнить. И если найдёте неточность в разборе заголовков или в поведении WARP, поправьте: провайдеры меняют политику без предупреждения, и то, что работало в октябре, к декабрю может устареть.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.