VPN·HUB·CRACK
СУПЕР-АВТОВЫБОР: балансер без обрывов + умный роутинг · VPN HUB CRACK
СУПЕР-АВТОВЫБОР: балансер без обрывов + умный роутинг
СУПЕР-АВТОВЫБОР
Обычный «Автовыбор» (leastPing) у многих работает пару минут, а потом заглохает, и помогает только перезапуск приложения. Это не случайность, а понятный баг конструкции. Ниже разберём, почему так и как собрать супер-балансер: он не залипает на задушенном транспорте, сам раскидывает нагрузку и умно роутит трафик (РФ-сервисы → мимо VPN на домашний IP, YouTube без рекламы, заграница → через быстрейшую ноду). Материал основан на реальном аудите стабильности боевого сервиса.
📖 Незнакомые слова? Все термины простыми словами смотрите в Словаре терминов.
🛠️ Конструктор конфига: заполни и скопируй
Не хочешь разбираться в тегах и стратегиях вручную, заполни поля ниже, нажми «Собрать» и скопируй готовый JSON. Ниже под ним написано, что с ним сделать в Remnawave.
DE, SE, NLПочему обычный «Автовыбор» работает → заглохает
- Балансер
leastPingвыбирает из пула член с минимальным пингом. В пуле обычно есть raw-TCP + Reality + Vision на :443, у него минимальный фрейминг, значит и минимальный пинг → leastPing всегда выбирает его. - ТСПУ (российское DPI-оборудование у операторов) по этому транспорту душит полосу (не рвёт коннект, а зажимает скорость почти в ноль).
observatory/burstObservatoryпингует мелкийgenerate_204раз в 30 с, и крошечная проба проходит даже на задушенном канале → leastPing никогда не понижает этот raw-член → «работает → заглохает».- Перезапуск приложения помогает, потому что новый коннект случайно садится на ещё живой транспорт.
Доказательство (замер с РФ-точки): один и тот же reality-ключ, 12 проб connect→speedtest→hold-60с. Vision-on-raw: ~4/12 удачных. gRPC: ~15/15. Отсюда: gRPC-first.
Лечение балансера
- Убрать raw+Vision из пула: оставить gRPC/xHTTP члены (порты 8443/2096). raw переживает блок хуже всех.
leastLoadвместо/поверхleastPing: он смотрит не на мелкую пробу, а на распределение задержек (baselines), и раскидывает шире, не залипая на одном члене.- Прямые EU-ноды (1 хоп), не каскад в дефолте. Замер: каскад душит upload (≈5 Мбит↑), прямая нода работает полноценно (90+↓/90+↑). Каскад держите как обход/fallback, но дефолтный пул: прямые DE/SE (gRPC+xHTTP).
- Блок QUIC (QUIC: быстрый транспорт поверх UDP, по сути «HTTP/3»)
udp:443 → blockПЕРЕД balancer-правилом. Иначе YouTube/видео залипает в туннеле (застрявший QUIC через высоколатентный канал: симптом «скачивание идёт, а видео нет»). Порт-scope (udp:443) не трогает Telegram-звонки/STUN/DNS (они не на 443). - Одна плитка на тариф. Юзер видит ОДИН «Автовыбор», а не 15 плиток, меньше путаницы, балансер сам берёт быстрейшее.
Умный роутинг: сердце супер-автовыбора
Правила идут сверху вниз, первое совпадение выигрывает. Порядок критичен:
- bittorrent → block (abuse от хостера).
- Реклама/трекеры → block (приятный бонус клиенту).
udp:443(QUIC) → block (см. п.4 выше).- TikTok → balancer (заграница). ВЫШЕ всех RU-правил, чтобы TikTok никогда не ушёл в direct (в РФ он замедлен/режется, нужен заграничный выход).
- YouTube → RU-нода (без рекламы + РФ-пиринг, см. «Каскад» про механику «YouTube без рекламы»).
- Сайты-проверки IP (
2ip.ru/2ip.io) → RU-нода: чтобы юзер видел российский IP и не пугался «у меня VPN палится». - private CIDR → direct.
- RU-сервисы + анти-фрод-домены → direct (домашний IP юзера): ключевое, разбор ниже.
- Всё остальное → balancer (заграница, leastLoad по прямым).
Анти-фрод: почему RU-сервисы идут DIRECT, а не через RU-ноду
Самый неочевидный и самый ценный приём.
- РФ-сайты палят VPN не по своему домену, а через анти-фрод-движки на ЗАРУБЕЖНЫХ доменах:
terratraf.io(Магнит),t-tech.team(Т-Банк),gravityfield.ai/diginetica.net(Дикси),appsflyer.com/onelink.me/branch.io/adjust.com(почти все мобильные аппы). - Если гнать их через вашу RU-ноду (маршрут по geoip назначения), зарубежный анти-фрод-домен уйдёт за границу → движок снова видит датацентровый немецкий IP → «VPN detected».
- Решение: эти домены →
direct= выход с домашнего резидентного IP юзера. Домашний IP > датацентрового IP сервера для анти-фрода. (На практике именноdirectчинил Магнит/Дикси.) - Вывод: в
directкладите не только.ru-домены, но и зарубежные домены РФ-сервисов (анти-фрод/аналитика/атрибуция аппов).
geo-dat-free: почему НЕ использовать geosite:/geoip:
- Правила роутинга выполняются на КЛИЕНТЕ, его файлами
geosite.dat/geoip.dat. - У части клиентов
geosite.datурезан → ядро падает:geosite.dat отсутствует секция YOUTUBE. Volumes с geo-файлами на нодах НЕ помогают: правила-то выполняются на клиенте! - Поэтому в шаблоне ноль ссылок
geosite/geoip, всё явными доменами/регексами/CIDR: geosite:youtube→youtube.com,googlevideo.com,ytimg.com,ggpht.com,youtubei.googleapis.com…geosite:category-ru→regexp:\.ru$|\.su$|\.xn--p1ai$+ явные не-.ru(vk.com,userapi.com,yastatic.net,wbstatic.net,ozonusercontent.com,avito.st+ анти-фрод-домены);geoip:private→ явные CIDR;geoip:ru→ убрать (РФ ловится поregexp:\.ru$; цена: RU-IP-only сайты без.ru-домена уйдут в туннель, но банки/госуслуги =.ru, ловятся).
Готовый шаблон (subscription template, geo-dat-free)
Кладётся в XRAY_JSON-шаблон подписки (subscription_templates), а не в Config Profile. proxy/ruyt инжектятся по тегам хостов.
🛑 КРИТИЧНО (проверено на Remnawave 2.7.4): шаблон обязан содержать готовый каркас xray: секции
dns,inboundsи непустой массивoutboundsс базовымиdirect(freedom)+block(blackhole). Remnawave НЕ добавляетdirect/blockсам, он лишь дописывает инжектируемыеproxy/ruytв уже существующийoutbounds. Еслиoutboundsв шаблоне нет, генератор падает сTypeError: template.outbounds is not iterable(в логах ядраXrayJsonGeneratorService), а клиенту уходит пустой ответ (HTTP 200,content-length: 0): «балансер рушит подписку». Именно поэтому ниже шаблон полный, а не толькоrouting+injectHosts.
{
"dns": { "servers": ["1.1.1.1", "1.0.0.1"], "queryStrategy": "UseIPv4" }, // только IPv4 — без зависаний на «битом» IPv6
"inbounds": [
{ "tag": "socks", "port": 10808, "listen": "127.0.0.1", "protocol": "socks",
"settings": { "udp": true, "auth": "noauth" },
"sniffing": { "enabled": true, "routeOnly": false, "destOverride": ["http", "tls", "quic"] } },
{ "tag": "http", "port": 10809, "listen": "127.0.0.1", "protocol": "http",
"settings": { "allowTransparent": false },
"sniffing": { "enabled": true, "routeOnly": false, "destOverride": ["http", "tls", "quic"] } }
],
"outbounds": [
{ "tag": "direct", "protocol": "freedom" },
{ "tag": "block", "protocol": "blackhole" }
],
"remnawave": {
"injectHosts": [
{ "selector": { "type": "tagRegex", "pattern": "^(DE|SE)$" }, "tagPrefix": "proxy", "selectFrom": "ALL" },
{ "selector": { "type": "tagRegex", "pattern": "^RUYT$" }, "tagPrefix": "ruyt", "selectFrom": "ALL" }
]
},
"burstObservatory": {
"subjectSelector": ["proxy"],
"pingConfig": { "destination": "https://www.gstatic.com/generate_204", "interval": "30s", "timeout": "3s", "sampling": 3 }
},
"routing": {
"domainStrategy": "AsIs",
"balancers": [
{
"tag": "AUTO",
"selector": ["proxy"],
"fallbackTag": "proxy",
"strategy": { "type": "leastLoad", "settings": { "expected": 3, "baselines": ["120ms", "250ms", "500ms"] } }
}
],
"rules": [
{ "type": "field", "protocol": ["bittorrent"], "outboundTag": "block" },
{ "type": "field", "outboundTag": "block",
"domain": ["doubleclick.net", "googlesyndication.com", "google-analytics.com", "mc.yandex.ru", "an.yandex.ru", "adfox.ru", "criteo.com", "taboola.com", "outbrain.com", "adriver.ru"] },
{ "type": "field", "network": "udp", "port": 443, "outboundTag": "block" },
{ "type": "field", "balancerTag": "AUTO",
"domain": ["tiktok.com", "tiktokv.com", "tiktokcdn.com", "byteoversea.com", "ibyteimg.com", "ibytedtos.com", "bytedance.com", "musical.ly"] },
{ "type": "field", "outboundTag": "ruyt",
"domain": ["youtube.com", "youtu.be", "googlevideo.com", "ytimg.com", "ggpht.com", "youtubei.googleapis.com", "youtube-nocookie.com"] },
{ "type": "field", "outboundTag": "ruyt", "domain": ["2ip.ru", "2ip.io"] },
{ "type": "field", "outboundTag": "direct", "ip": ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] },
{ "type": "field", "outboundTag": "direct",
"domain": [
"regexp:\\.ru$", "regexp:\\.su$", "regexp:\\.xn--p1ai$",
"vk.com", "vk.me", "userapi.com", "yandex.net", "yastatic.net", "ozonusercontent.com", "wbstatic.net", "avito.st",
"terratraf.io", "t-tech.team", "gravityfield.ai", "diginetica.net", "retailrocket.net", "flocktory.com", "mindbox.cloud",
"appsflyer.com", "onelink.me", "branch.io", "adjust.com", "cloudpayments.ru", "qiwi.com",
"ip-api.com", "ipify.org", "ipinfo.io"
] },
{ "type": "field", "network": "tcp,udp", "balancerTag": "AUTO" }
]
}
}
Что поменять под себя: теги в injectHosts (^(DE|SE)$: ваши прямые EU-ноды; ^RUYT$: RU-нода под YouTube), списки доменов (рекламу/RU-сервисы дополняйте своими). direct/block уже в шаблоне (трогать не нужно).
Как читать injectHosts (частый вопрос: «а через запятую?»)
injectHosts это массив пулов [ … ]. Каждый { … } внутри задаёт один пул:
selector.patternзадаёт регэксп, который матчится на поле Tag хоста в панели (именно Tag хоста, а не домен и не адрес);tagPrefixопределяет, как назвать полученные outbound'ы:proxy→proxy,proxy-2,proxy-3… (по числу совпавших хостов). Балансер ловит их по префиксу:selector: ["proxy"];selectFrom: "ALL"означает: берём все хосты, совпавшие по тегу (∩ инбаунды сквода юзера).
Главное правило: запятая разделяет разные пулы, а несколько тегов в один пул задаются через | внутри ^( … )$.
- Несколько заграничных нод в один автовыбор → расширь регэксп, а не плоди объекты:
{ "selector": { "type": "tagRegex", "pattern": "^(DE|SE|NL)$" }, "tagPrefix": "proxy", "selectFrom": "ALL" } - Отдельный пул (RU-нода под YouTube) → добавляй новым объектом через запятую:
"injectHosts": [
{ "selector": { "type": "tagRegex", "pattern": "^(DE|SE)$" }, "tagPrefix": "proxy", "selectFrom": "ALL" },
{ "selector": { "type": "tagRegex", "pattern": "^RUYT$" }, "tagPrefix": "ruyt", "selectFrom": "ALL" }
]
Твой ^ATCP$ = только хосты с Tag ровно ATCP. Одна такая нода: оставь как есть; если заграничных нод несколько, перечисли их теги: ^(ATCP|DE2|NL)$ (и проставь эти теги в поле Tag у каждого хоста, иначе он не попадёт в пул).
⚠️ Один нюанс с YouTube-пулом. Правило
"outboundTag": "ruyt"бьёт только в первый инжектнутый outbound (ruyt). Если RU-нод под YouTube несколько (^RUYT$совпал с двумя+), заведи для них отдельный балансер, как AUTO:{ "tag": "RUYT", "selector": ["ruyt"], "strategy": { "type": "random" } }и роуть YouTube через"balancerTag": "RUYT"вместоoutboundTag. Для одной RU-ноды оставляйoutboundTag: "ruyt", так проще.✅ Проверка перед заливкой: прогоните рендер через
xray -test -config <render.json>в каталоге без geo-файлов (XRAY_LOCATION_ASSET=/tmp/empty): должно бытьConfiguration OKи ноль ссылокgeosite:/geoip:. Так ловите и «не iterable» (пустой ответ), и geo-краш на урезанном клиенте за один шаг.⚠️ Синтаксис селектора (проверено на 2.7.4):
"selector": { "type": "tagRegex", "pattern": "^TAG$" }: именноtype+pattern, не{ "tagRegex": "…" }. И главное: у каждого хоста в пуле должно стоять полеtag= вашему паттерну (DE/SE/RUYT). Тег пуст хоть у одного, он не попадёт в балансер; теги пусты у всех, пул пустой, балансер ни к кому не подключится. (Самая частая причина «автовыбор не работает».)
Где это живёт и главные грабли (Remnawave)
- Балансер и роутинг живут в
subscription_templates.template_json(XRAY_JSON-шаблон подписки), а не в Config Profile и не черезhosts.is_hidden. - 🛑 Ошибка
Invalid protocol in inbound "socks". Allowed values are: … vless, wireguardпри сохранении = вставил не туда. Это валидатор Config Profile / инбаундов ноды: там разрешены только серверные протоколы (vless / trojan / shadowsocks / …), аsocksиhttpиз шаблона это клиентские (инбаунды для приложения юзера, не для ноды). Весь этот JSON целиком идёт ТОЛЬКО в Подписка → Шаблоны → XRAY_JSON, там инбаунды не валидируются. Config Profile для автовыбора не трогаешь вообще. (Это самая частая причина «шаблон не сохраняется / не то».) - ⚠️ Главный урок: пул задаётся
routing.balancers[].selector(префикс["proxy"]тянетproxy/proxy-2/…), а НЕ строкамиhosts.UPDATE hosts SET is_hiddenубирает плитку у юзера, но НЕ убирает задушенный raw-outbound из серверного пула → правьте шаблон, а не прячьте хост. injectHostsсквод-скоупный: пулselectFrom:ALL= (хосты по тегу) ∩ (инбаунды сквода юзера) − (exclusions).is_hidden-хост остаётся в пуле, но не плиткой; excluded-хост не в пуле и не плиткой.- «Одна плитка на тариф» делается через
internal_squad_host_exclusions: у тарифа скрываете все плитки, кроме его «Автовыбора». Чтобы обход был в пуле, но не плиткой, нужныis_hiddenBYPASS-хосты (а не exclude). - ⚠️ HWID-заглушка путает при отладке. Если включён HWID, проверка подписки
curl'ом без заголовкаx-hwidотдаёт болванкуvless://…@0.0.0.0:1#App not supported. Это не «нет хостов» и не поломка, аHWIDNotSupported. Проверяйте с реальнымx-hwidустройства (из таблицыhwid_user_devices) либо админскимGET /api/subscriptions/by-short-uuid/<id>: он HWID игнорит и показывает настоящие ссылки. - ⚠️ Домен vs IP (sniffing). Правила по домену (
2ip.ru, анти-фрод-домены) срабатывают, только если клиент снифит домен из трафика. v2rayTun на Android в дефолте не снифит → сервис на зарубежном IP уедет в туннель мимоdirect/ruyt(наглядно:2ip.ruживёт на немецком Hetzner188.40.167.82, а не на РФ-IP, потому на Android и кажет сервер, а на iOS всё ок). Лечится на клиенте стеком gVisor (Настройки трафика). Для подстраховки на сервере ключевые чек-сервисы кладите и по IP, не только по домену:{ "type": "field", "outboundTag": "ruyt", "ip": ["188.40.167.82/32"] }для 2ip.ru. IP-правило от sniffing не зависит и работает на любом клиенте.
Доставка шаблона в приложение: без неё всё впустую
Шаблон можно собрать идеально, проставить теги, а у клиента 2ip всё равно кажет сервер. Причина почти всегда одна: приложение получает не ваш XRAY_JSON, а base64-список: голые vless://… без секции routing = full-tunnel, весь трафик в туннель, ни РФ-direct, ни балансера.
- Формат ответа Remnawave выбирает по User-Agent (
subscription_settings.response_rules). Из коробки правила есть под Clash/Mihomo/Sing-box/Stash + Fallback Base64. Под последний попадают Happ, v2rayTun, v2rayNG → им уходит base64 без роутинга. - Фикс: добавьте правило
User-Agent ~ ^(happ|v2raytun|v2rayng) → XRAY_JSONвыше Fallback Base64 (Subscription settings → Response Rules). Теперь эти клиенты получают шаблон с роутингом и балансером. - Альтернатива:
serve_json_at_base_subscription = true(json по базовому URL всем json-совместимым), но точечное правило по UA безопаснее (не заденет тех, кому реально нужен base64). - Happ отдельно: при JSON-подписке Happ запускает xray-core «как есть» и берёт правила только из самого JSON: поле
happ_routingдля json не применяется (оно для base64/uri). После включения XRAY_JSON клиенту лучше удалить и заново добавить подписку в Happ: простой «обновить» не всегда переключает закэшированный base64 → json.
Проверка
- 2ip / спидтест → показывает РФ-IP (через
ruyt), юзер не пугается «VPN палится». - YouTube → грузится без рекламы (РФ-IP).
- Банк / Магнит / Дикси / аппы → открываются (через
direct, домашний IP: анти-фрод не срабатывает). - Заграница (Google/Discord) → через быстрейшую DE/SE-ноду.
- При обрыве →
leastLoadсам пересаживает; raw больше не залипает.
Связано: базовые стратегии балансеров → «Балансировщики»; список RU-direct доменов → «Маршрутизация»; механика «YouTube без рекламы» → «Каскад». Клиентский симптом «заглохает» до конца лечит in-tunnel health-probe в приложении (периодическая проба сквозь туннель → форс-реконнект при N фейлах).