Перейти к содержанию

Профили маршрутизации

Профиль маршрутизации — это полный набор правил: что идёт напрямую, что через туннель и что блокируется, плюс опциональный split-DNS. Это ответ на «у меня подписка — одна ссылка vless://, внутри неё нет маршрутизации»: профиль несёт маршрутизацию отдельно от серверов.

Совместимость формата. Формат профиля тот же, что использует Happ, включая имена полей. Это сделано намеренно: профили ходят между приложениями ссылками и файлами, и совместимость важнее своих названий.

Профиль

{
  "Name": "Bypass RU",
  "GlobalProxy": true,
  "DirectSites": ["geosite:category-ru", "domain:gosuslugi.ru"],
  "DirectIp":    ["geoip:ru", "192.168.0.0/16"],
  "ProxySites":  ["geosite:youtube"],
  "BlockSites":  ["geosite:category-ads-all"],
  "RemoteDNSType": "DoH", "RemoteDNSDomain": "https://dnsforge.de/dns-query",
  "DomesticDNSType": "DoU", "DomesticDNSIP": "77.88.8.8",
  "DnsHosts": { "example.com": "1.2.3.4" },
  "DomainStrategy": "IPIfNonMatch",
  "UpdateUrl": "https://example.com/routing.json",
  "UpdateInterval": 24,
  "LastUpdated": 1756600000
}
Поле Значение
Name Имя профиля. Оно же — ключ обновления: импорт профиля с тем же именем заменяет старый, а не создаёт дубликат.
GlobalProxy Куда идёт трафик, не попавший ни в одно правило: true — через туннель, false — напрямую.
DirectSites / ProxySites / BlockSites Домены и категории: geosite:ru, domain:example.com, regexp:…
DirectIp / ProxyIp / BlockIp IP-адреса, подсети и geoip:ru.
RemoteDNS* / DomesticDNS* Split-DNS: удалённый резолвер отвечает за проксируемые имена, локальный — за прямые. Шесть полей — см. «Поля split-DNS» ниже.
DnsHosts Статические соответствия домен → IP.
DomainStrategy Порядок сопоставления правил: AsIs (по умолчанию), IPIfNonMatch, IPOnDemand.
FakeDNS Принимается для совместимости (true/false, булево или строкой, как у Happ); ядро SMProxy FakeDNS не использует, значение игнорируется.
UpdateUrl Откуда брать свежую копию профиля. С ним профиль самодостаточен: раздавайте его как base64 или файл, и он всё равно будет обновляться.
UpdateInterval Как часто обновлять, в часах. 12…168 (неделя); по умолчанию 24.
LastUpdated Необязательное. Unix-время последнего изменения профиля — число или строка с числом, как в Happ. Показывается в карточке профиля и работает как защита от отката версии — см. ниже.

Правила применяются в порядке block → direct → proxy и ставятся перед правилами, пришедшими с конфигурацией сервера.

GlobalProxy и FakeDNS принимаются и как JSON-булево, и как строки "true" / "false", которые шлют Happ и INCY. Поля, которые другие клиенты добавляют для своего интерфейса — RouteOrder, UseChunkFiles — игнорируются: SMProxy всегда применяет правила в порядке block → direct → proxy.

Поля split-DNS

Поле Значение
RemoteDNSType Протокол удалённого резолвера: DoH, DoT, DoQ или DoU (обычный UDP).
RemoteDNSDomain Адрес резолвера — URL DoH/DoT, например https://dnsforge.de/dns-query. Пусто для обычного UDP.
RemoteDNSIP IP резолвера. Вместе с RemoteDNSDomain это bootstrap-адрес домена — та же роль, что у ;ip=… в заголовке s-resolve; без домена — обычный UDP-резолвер.
DomesticDNSType / DomesticDNSDomain / DomesticDNSIP Те же три поля для локального резолвера (прямые имена).
RemoteDns / DomesticDns Устаревшее написание из старых профилей Happ/INCY — синоним RemoteDNSIP / DomesticDNSIP. Читается только если новое поле отсутствует или пустое. В новых профилях не использовать.

Как используются резолверы:

  • Кто за что отвечает. Remote — имена, идущие через туннель, Domestic — имена, идущие напрямую (DirectSites). Удалённый резолвер опрашивается через туннель, локальный — через физическую сеть.

  • RemoteDNSDomain — ещё и собственный резолвер клиента для служебных запросов (обновление подписки, загрузка профиля), когда подписка не присылает s-resolve. Профиль маршрутизации, таким образом, задаёт DNS для всего, а не только для трафика внутри туннеля.

  • Bootstrap. Суффикс ;ip=… из s-resolve не работает внутри RemoteDNSDomain — там нет разбора, адрес уйдёт с мусором на конце. Bootstrap-адрес кладите в RemoteDNSIP.

  • Цепочка запасных. Сначала резолверы профиля, затем DNS-серверы из самого конфига подключения (их разбиения domains / skipFallback сохраняются). Если все резолверы профиля недоступны, разрешение имён уходит на серверы конфига.

  • Поддержка массивов клиентами: SMProxy начиная с Android 411 / iOS, macOS, tvOS 305 / Windows, Linux 193. Happ, INCY и старые сборки SMProxy читают только строку — присылайте строку, если не уверены, что пользователи обновились.

Строка или массив — расширение SMProxy. RemoteDNSDomain, RemoteDNSIP, DomesticDNSDomain и DomesticDNSIP принимают либо одну строку (классический формат, совместимый с Happ/INCY), либо массив строк:

"RemoteDNSDomain": ["https://dns1.example/dns-query", "https://dns2.example/dns-query"],
"RemoteDNSIP":     ["1.2.3.4", "5.6.7.8"]

Порядок = приоритет: ядро опрашивает резолверы сверху вниз, вторая запись — запасная для первой. *DNSIP[i] — bootstrap-адрес для *DNSDomain[i]; IP без парного домена работает как обычный резолвер.

Защита от отката — LastUpdated

Когда профиль с тем же Name приходит автоматически (заголовки подписки, обновление по UpdateUrl, ссылка), он заменяет сохранённый только если его LastUpdated больше. Если метки нет с одной из сторон, профиль заменяется безусловно. Увеличивайте значение при каждом изменении профиля. Ручной импорт из интерфейса приложения применяется всегда. 🧩 — Не пишите JSON руками — Конструктор соберёт профиль из обычных полей и сразу даст JSON, ссылку smproxy://routing/onadd/… и QR-код. Всё считается в браузере. — Конструктор профиля →

Как передать профиль пользователю

Ссылкой — самый естественный способ: из браузера, из сообщения или по QR-коду.

Ссылка Что делает
smproxy://routing/add/<base64\|url> Импортировать профиль, не переключаясь на него
smproxy://routing/onadd/<base64\|url> Импортировать и включить
smproxy://autorouting/add/<url> · …/onadd/<url> То же плюс обновление с этого URL
smproxy://routing/off Выключить маршрутизацию

Полезную нагрузку можно передать и как ?data=<base64>. URL может вернуть JSON профиля, его base64 или другую ссылку smproxy:// — принимаются все три варианта.

Заголовком подписки — профиль приходит вместе с самой подпиской.

Заголовок Назначение
routing Профиль: base64, обычный JSON или URL, откуда его забрать. Обновляется вместе с подпиской.
autorouting URL, откуда забрать профиль и обновлять его по собственному расписанию.
routing-update-url Откуда обновлять профиль — если не хочется класть UpdateUrl внутрь самого профиля.
routing-update-interval Интервал обновления в часах (12…168). Значения вне диапазона подрезаются.
routing-profile Какой профиль должна использовать эта подписка — по имени.

Профиль, пришедший с подпиской, добавляется в список, но не включается — выбор остаётся за пользователем. Эти ключи работают и без Provider ID.

Заголовки принимают и deep-link-команды. Значением routing / autorouting может быть deep-link-команда — в тех же формах, что кликабельные ссылки, с любым словом схемы (smproxy://, happ://, yourapp://): внутри заголовка слово перед :// не важно.

Значение заголовка Действие
*://routing/add/<base64\|url> Импортировать профиль в список, не назначать его
*://routing/onadd/<base64\|url> Импортировать и назначить этой подписке
*://autorouting/add/<url> · …/onadd/<url> То же плюс поддерживать профиль в актуальном состоянии по этому URL
*://routing/off Отвязать профиль от этой подписки — провайдер выключает её маршрутизацию

Полезная нагрузка может быть зашифрована как crypt1/<base64> — как и любое другое значение заголовка.

Те же команды принимаются и отдельной строкой в теле подписки, прямо среди ссылок на серверы (совместимо с INCY):

vless://uuid@server1:443?security=tls#Server1
vmess://eyJhZGQiOiAic2VydmVyMi...
incy://routing/onadd/ewogICJOYW1lIjogIl...

Настоящий HTTP-заголовок или явный заголовок в теле #routing: важнее строки без префикса.

Глобально и по подписке

Пользователь выбирает один активный профиль в настройках — это глобальный выбор. Подписка может его переопределить: если в ней указан профиль (в её настройках или заголовком routing-profile), при подключении к её серверам применяется он. И то, и другое можно выключить: глобально в настройках (или ссылкой routing/off), а для подписки — вариантом «Не применять».

Какой профиль победит

При обновлении подписки несколько заголовков выше могут прийти одновременно. Они разбираются в таком порядке:

1 routing / autorouting — профиль, присланный целиком, важнее скачанного по routing-update-url. Присылайте сам профиль, если хотите быть уверены, какая именно версия окажется у клиента.

2 только routing-update-url — если профиль не пришёл целиком, клиент скачает его по этому адресу и импортирует. Заголовок работает сам по себе — routing рядом не нужен.

3 routing-profile (имя) — определяет, каким профилем подписка пользуется, и перебивает всё, что пришло в этом обновлении. Если указать Corporate и одновременно прислать профиль Default, подписка возьмёт Corporate, а Default всё равно попадёт в список.

Имя, которому ничего не соответствует, ничего не меняет. Если пришёл routing-profile: Corporate, а профиля с таким именем на устройстве нет, подписка сохранит прежние настройки маршрутизации. Клиент не подставит другой профиль и не сбросит настройку — опечатка в заголовке не может незаметно перенаправить трафик ваших пользователей.

Глобальный выбор не трогается никогда. Всё описанное выше задаёт профиль для этой подписки. Профиль, выбранный пользователем в настройках, остаётся как есть и продолжает действовать для всех остальных подписок. Чтобы сменить профиль подписки, используйте routing-profile; заголовка, меняющего глобальный выбор пользователя, нет.

Изменения применяются при следующем подключении. Профили читаются при сборке конфигурации туннеля, поэтому профиль, пришедший на активном соединении, начнёт действовать после переподключения — обновление подписки на ходу не перенаправляет живой трафик.

Какой резолвер используется, по порядку

заголовок s-resolve → поле RemoteDNSDomain активного профиля маршрутизации → настройка пользователя → встроенный список (если пользователь его включил) → системный резолвер.

s-resolve: off отключает предразрешение целиком — профиль в этом случае не подхватится.