NaiveProxy: частые ошибки и решения
NaiveProxy заметно надёжнее многих протоколов обхода блокировок именно потому, что маскируется под обычный HTTPS-трафик Chrome, а не изобретает свой узнаваемый почерк. Но у этой надёжности есть цена: конфигурация завязана на TLS-сертификат, домен-прикрытие и basic auth одновременно, и ошибка в любом из трёх звеньев рвёт всё соединение без внятного сообщения на клиенте. Разберём, где чаще всего ломается и как это чинить.
Содержание
- Как устроен NaiveProxy и почему ошибки типовые
- TLS-сертификат: handshake failed, просрочка, самоподписанный
- Домен-прикрытие: как выбрать и не спалить прокси
- Basic Auth: неверные учётные данные и постоянные 401/407
- Caddyfile: сборка, версия и типичные опечатки конфигурации
- Диагностика за 5 минут: curl, openssl, логи
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Как устроен NaiveProxy и почему ошибки типовые
NaiveProxy — это не отдельный протокол, а связка: клиент использует сетевой стек Chromium (тот же QUIC/HTTP2, те же TLS-отпечатки, что у настоящего Chrome), а на сервере поднимается Caddy с плагином forward proxy (forwardproxy, форк от klzgrad). Снаружи это выглядит как обычный HTTPS-сайт на 443 порту. Запрос без правильных заголовков авторизации Caddy отдаёт как обычный веб-сервер — статику или редирект, а запрос с корректным Basic Auth превращается в туннель прокси.
Отсюда и три точки отказа:
- TLS — если сертификат невалиден, до авторизации дело не доходит вообще, клиент отваливается на handshake.
- Домен-прикрытие — если сайт-обёртка выглядит подозрительно или отсутствует, DPI и активное зондирование палят прокси даже при рабочем TLS.
- Basic Auth — если логин/пароль или их кодирование не совпадают между клиентом и Caddyfile, туннель просто не поднимается, а ошибка на клиенте похожа на сетевую.
Типовой минимальный Caddyfile на сервере выглядит так:
example.com {
route {
forward_proxy {
basic_auth user_name strong_password_here
hide_ip
hide_via
probe_resistance
}
file_server {
root /var/www/html
}
}
}
И клиентский конфиг (для официального клиента naive или для v2rayN/NekoRay с поддержкой naive):
{
"listen": "socks://127.0.0.1:1080",
"proxy": "https://user_name:strong_password_here@example.com"
}
Держите оба файла рядом при диагностике — почти все ошибки видны на стыке этих двух конфигов.
TLS-сертификат: handshake failed, просрочка, самоподписанный
Caddy по умолчанию сам получает сертификат Let's Encrypt при старте — это удобно, пока что-то не пошло не так.
Caddy не может выпустить сертификат. Самая частая причина — 80/443 порт занят другим процессом (nginx, apache, старый Caddy) или DNS A-запись ещё не указывает на сервер. Проверьте:
sudo ss -tlnp | grep -E ':80|:443'
dig +short example.com
Если порт занят — остановите конфликтующий сервис (systemctl stop nginx) перед первым запуском Caddy, либо явно укажите Caddy слушать через уже настроенный reverse proxy — но для NaiveProxy это усложняет схему, проще отдать 443 напрямую Caddy.
Сертификат просрочен. Caddy обновляет сертификаты автоматически примерно за 30 дней до истечения, но если сервер был выключен долгое время или ACME-запрос стабильно фейлится (например, файрвол блокирует исходящие на порт 80 для HTTP-01 challenge), сертификат протухает. Проверить срок действия:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
Если notAfter уже в прошлом — смотрите логи Caddy (journalctl -u caddy -n 100 --no-pager) на предмет ошибок ACME и убедитесь, что 80 порт открыт наружу хотя бы для HTTP-01 challenge, либо переключитесь на DNS-01 через провайдера DNS, если 80-й порт для вас в принципе закрыт.
Клиент ругается на handshake, хотя сертификат в порядке. Частая причина — в proxy URL клиента указан IP-адрес вместо доменного имени. TLS SNI при подключении по IP не совпадёт с именем в сертификате, и handshake упадёт. Прокси-строка обязана содержать домен:
"proxy": "https://user:pass@example.com"
не https://user:pass@1.2.3.4.
Если вы сравниваете подходы к выпуску и продлению сертификатов вообще (не только для NaiveProxy), у нас есть отдельный разбор certbot или acme.sh — что выбрать для сервера, а типовые причины, по которым nginx не подхватывает SSL, разобраны в статье про nginx не применяет SSL — логика ACME там пересекается с Caddy почти один в один.
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверДомен-прикрытие: как выбрать и не спалить прокси
Смысл probe_resistance и file_server в конфиге — в том, что при обращении без валидного Basic Auth сервер должен отвечать как обычный сайт, а не «прокси, который просит пароль». Если этого не сделать правильно, активное зондирование (когда цензор сам стучится на ваш IP и смотрит на ответ) моментально отличает NaiveProxy от настоящего сайта.
Типичные ошибки на этом уровне:
- Пустой или дефолтный
file_server. Каталог/var/www/htmlпуст или содержит страницу-заглушку по умолчанию — для зонда это подозрительный сигнал, «живые» сайты так не выглядят. Разверните туда минимально правдоподобный статический сайт: несколько страниц, картинки, реальныйfavicon.ico. - Домен без истории. Только что зарегистрированный домен без DNS-истории и без индексации в поисковиках повышает подозрительность при ручной проверке. Лучше прогреть домен заранее: поставить на него реальный, пусть и простой, сайт за неделю-две до использования под NaiveProxy.
- Поддомен, который явно указывает на инфраструктуру.
vpn.example.com,proxy.example.com,naive.example.com— такие имена видно в логах Certificate Transparency любому, кто мониторит выпуск сертификатов по шаблону. Используйте нейтральное имя без намёков на назначение. - Домен уже в блок-листах. Проверяйте домен перед использованием — если он уже засвечен на другом прокси или сайте, который блокировали, DPI может резать его по SNI независимо от корректности NaiveProxy.
- Несовпадение
probe_resistanceс реальным поведением сайта. Эта опция делает так, что запросы без авторизации получают обычный ответ сайта, а не характерную ошибку прокси. Без неё Caddy может отдавать заголовки, которые совпадают с сигнатурой forwardproxy — задокументированный маркер для DPI-эвристик.
Если вы выбираете, где физически разместить такой сервер — от юрисдикции и качества сети зависит и устойчивость домена к блокировкам. Сравнение направлений есть в статье США или Великобритания: где брать сервер для VPN и обхода блокировок.
Basic Auth: неверные учётные данные и постоянные 401/407
Здесь ошибки почти всегда банальные, но их сложно отличить от сетевых на глаз.
Логин или пароль не совпадают. Проверяйте построчно: basic_auth в Caddyfile принимает логин и пароль как есть, без хеширования (это не аутентификация самого Caddy как веб-сервера, а параметр forwardproxy). Один лишний пробел или невидимый символ при копировании из мессенджера — и туннель не поднимется, а клиент покажет что-то вроде обрыва соединения, не сообщая про 407.
Спецсимволы в пароле не заэкранированы в URL клиента. Если пароль содержит @, :, /, # или %, их обязательно нужно процентно кодировать в proxy-строке клиента:
пароль: P@ss:word/1
в URL: https://user_name:P%40ss%3Aword%2F1@example.com
Проще всего сразу генерировать пароль без спецсимволов — только буквы и цифры, но длиной от 20 символов, чтобы компенсировать упрощённый алфавит:
openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 24
После смены пароля Caddy не перечитал конфиг. caddy reload не всегда подхватывает изменения в блоках с плагинами так же чисто, как штатные директивы. Если после правки Caddyfile авторизация продолжает падать со старыми учётными данными — делайте полный systemctl restart caddy, а не reload.
Порядок директив в блоке route. forward_proxy обязан идти раньше file_server в блоке route { }. Если поменять местами, Caddy сначала попытается отдать статику по любому запросу, включая CONNECT-запросы прокси, и авторизация просто не будет вызываться — внешне это выглядит как «прокси не отвечает», хотя сервер работает и сертификат в порядке.
Caddyfile: сборка, версия и типичные опечатки конфигурации
Форк forwardproxy не входит в официальную сборку Caddy — его нужно собрать через xcaddy с явным указанием модуля:
xcaddy build --with github.com/klzgrad/forwardproxy@naive
Частые проблемы на этом шаге:
- Собрали Caddy без плагина. Директива
forward_proxyв конфиге вызывает ошибкуunrecognized directiveпри старте — бинарник собран стандартной сборкой без модуля. Проверьте:caddy list-modules | grep forwardproxy. - Версия Caddy и версия форка разъехались. Форк
klzgrad/forwardproxyпривязан к веткеnaive, обновление Caddy мажорной версией без пересборки форка иногда ломает совместимость API. Если сервис не стартует послеapt upgrade— пересоберите черезxcaddyзаново. hide_ipиhide_viaне добавлены. Без них сервер добавляет заголовкиX-Forwarded-ForиVia, которые выдают факт проксирования и реальный IP клиента. Для маскировки под обычный сайт эти опции обязательны.- Systemd-юнит не рестартует сервис при падении. Если Caddy падает по OOM или из-за временной сетевой ошибки ACME, а
Restart=on-failureне прописан в юните — сервис просто остаётся лежать. ДобавьтеRestart=on-failureиRestartSec=5в секцию[Service], если строк нет.
Таблица типичных симптомов и вероятной причины — для быстрой сверки:
| Симптом на клиенте | Вероятная причина |
|---|---|
| TLS handshake failed сразу | Сертификат не выпущен / просрочен / проксирование по IP вместо домена |
| Соединение открывается, но сразу рвётся | Неверный логин/пароль, порядок директив в route |
| Работает, но подозрительно медленно и палится через время | Домен без истории, нет file_server с реальным контентом |
| Caddy не стартует после правки конфига | Опечатка в Caddyfile, forward_proxy без установленного модуля |
| Работает локально, не работает после обновления Caddy | Форк forwardproxy не пересобран под новую версию |
Диагностика за 5 минут: curl, openssl, логи
Прежде чем гадать, проверьте три вещи по порядку — это отсекает 90% причин.
1. TLS вообще поднимается?
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | grep -E 'Verify return|subject='
Verify return code: 0 (ok) — сертификат в порядке. Если код другой — проблема в TLS, до Basic Auth дело не дойдёт в принципе.
2. Сайт-прикрытие отвечает как обычный сайт?
curl -sI https://example.com/
Ожидаем 200 OK с обычными веб-заголовками. Если видите что-то похожее на ошибку прокси или пустой ответ — проблема в file_server/probe_resistance.
3. Туннель поднимается с правильными данными?
curl -x socks5h://127.0.0.1:1080 https://ifconfig.me
Если запущен клиент naive с локальным SOCKS5 на 1080 — этот запрос должен вернуть IP вашего сервера, а не локальный. Если curl висит и таймаутит — смотрите логи клиента (обычно verbose-режим включается флагом при запуске бинарника naive) и логи сервера:
journalctl -u caddy -f
В логах Caddy при неудачной авторизации будет видно 401, при проблемах с TLS — ошибки на уровне обработки соединения ещё до записи в access-лог.
Если после этих трёх шагов TLS и сайт-прикрытие в порядке, а туннель всё равно не поднимается — дело почти всегда в Basic Auth или в порядке директив route.
Арендуйте сервер под свои задачи!
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
NaiveProxy можно поставить на nginx вместо Caddy?
Нет, forwardproxy — плагин именно для Caddy. Под nginx готовой реализации NaiveProxy нет; ближайшие альтернативы с похожей маскировкой под HTTPS — Xray с VLESS+Reality или Trojan.
Почему клиент подключается, но интернет через прокси не работает?
Чаще всего дело не в NaiveProxy, а в маршрутизации на клиенте: SOCKS5-порт слушается, но приложение не настроено его использовать, либо DNS-запросы идут мимо прокси. Проверьте системные настройки прокси и DNS-over-HTTPS в браузере.
Обязательно ли включать probe_resistance?
Формально нет, но без неё сервер отвечает на неавторизованные запросы предсказуемым образом, отличным от обычного сайта — это упрощает автоматическое обнаружение прокси при зондировании.
Сертификат от Let's Encrypt подозрителен сам по себе?
Нет, большинство обычных сайтов используют Let's Encrypt. Подозрение вызывает не факт его использования, а домен без истории и без реального контента за ним.
Как часто нужно менять домен-прикрытие?
Жёсткого правила нет — меняйте при признаках блокировки по SNI или попадании в публичные блок-листы. Профилактически часто менять не имеет смысла: прогретый домен ценнее свежего.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →