Идея и грабля
Хочется резать рекламу на уровне VPN правилом geosite:category-ads-all в routing. Естественная мысль — добавить его в клиентский шаблон подписки, чтобы блок работал у всех. Так делать нельзя: это кладёт мобильных клиентов. Правильное место — серверный профиль ноды.
Почему клиентский шаблон ломается
В geosite.dat, который несёт мобильный клиент, набор урезанный — секции category-ads-all там может не быть. Xray-core 26.x не игнорирует неизвестный geosite-тег: он роняет парсинг всего routing, ядро не стартует, VPN не поднимается вообще. Тот же класс бага, что geoip:ru при включённом «Truncate geofiles» у клиента.
На ноде наоборот: geosite.dat в образе полный (10+ МБ), тег есть. А если в какой-то сборке его нет — правило на ноде молча становится no-op, ничего не ломая. Fail-safe там, где нам и нужно.
Как сделать
Правило кладём в config-профиль нод, не в шаблон подписки:
{ "type": "field", "domain": ["geosite:category-ads-all"], "outboundTag": "BLOCK" }
Тег outbound — как в конкретном профиле (blackhole). Реклама режется на сервере до выхода, работает для всех клиентов разом — iOS, Android, sing-box — и не требует обслуживания.
Порядок правил важен: Xray выбирает первое совпадение.
- В основном профиле — правило ставим последним, после списка DIRECT-доменов, чтобы локальные сервисы (включая рекламные поддомены нужных сайтов) оставались приоритетнее.
- В балансир-профиле — перед catch-all правилом с
balancerTag, иначе до adblock просто не дойдёт.
Применение через API: обёртки панелей нередко принимают только name и config через них не поменять — рабочий путь это GET профиля → добавить правило в config.routing.rules → PATCH профиля целиком. Снапшот до правки обязателен. Применять по одному профилю, между ними проверять, что ноды isConnected и онлайн восстановился: после пуша xray рестартует, online на 10–25 секунд падает в ноль — это норма.
Подводные камни
- Неизвестный geosite-тег в клиенте — падение всего ядра, не no-op. Главное, что надо запомнить из этой истории.
- Шаблон sing-box — другой синтаксис (
route.rules+ rule_set), это отдельная задача. - Перед записью — дедуп: проверить, нет ли уже такого правила, чтобы не задваивать.
Вывод. geosite:category-ads-all режет рекламу на уровне VPN, но только на ноде, где geosite.dat полный. В клиентском шаблоне неизвестный тег роняет ядро и кладёт VPN целиком. Кладите правило в серверный профиль — последним в основном, перед balancerTag в балансире — правьте через GET→append→PATCH со снапшотом, и adblock работает для всех клиентов без обслуживания.