Профили маршрутизации¶
Профиль маршрутизации — это полный набор правил: что идёт напрямую, что через туннель и что блокируется, плюс опциональный split-DNS. Это ответ на «у меня подписка — одна ссылка vless://, внутри неё нет маршрутизации»: профиль несёт маршрутизацию отдельно от серверов.
★ Совместимость формата. Формат профиля тот же, что использует Happ, включая имена полей. Это сделано намеренно: профили ходят между приложениями ссылками и файлами, и совместимость важнее своих названий.
Профиль¶
{
"Name": "Bypass RU",
"GlobalProxy": true,
"useChunkFiles": true,
"useTags": ["geoip:ru", "geosite:youtube"],
"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://doh.ffmuc.net/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 — напрямую. |
useChunkFiles / UseChunkFiles |
По умолчанию false. Принимает true/false и строки "true"/"false". Передаёт ядру только категории из правил и useTags. Поддерживается на Apple с 2.0.21 (364), Android с 2.0.6 (493); Windows/Linux используют полные базы. |
useTags / UseTags |
По умолчанию []. Дополнительные категории с префиксами geoip: / geosite:, например ["geoip:ru", "geosite:youtube"]. Действует только при useChunkFiles: true; не создаёт правил. Отсутствующие категории пропускаются, если не используются в правилах. |
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 — игнорируются: SMProxy всегда применяет правила в порядке block → direct → proxy.
Поля split-DNS¶
| Поле | Значение |
|---|---|
RemoteDNSType |
Протокол удалённого резолвера: DoH, DoT, DoQ или DoU (обычный UDP). |
RemoteDNSDomain |
Адрес резолвера — URL DoH/DoT, например https://doh.ffmuc.net/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 отключает предразрешение целиком — профиль в этом случае не подхватится.
Сокращённые геобазы: useChunkFiles и useTags¶
Поддерживается на iOS/iPadOS, macOS (Mac Catalyst) и tvOS с 2.0.21 (364), на Android/Android TV с 2.0.6 (493). Windows и Linux не изменены: используют полные базы, эти поля там не действуют.
| Поле | По умолчанию | Назначение |
|---|---|---|
useChunkFiles |
false |
Принимает JSON true/false и строки "true"/"false". Включает вырезание категорий перед передачей баз Xray. Совместимое имя: UseChunkFiles. |
useTags |
[] |
Дополнительные категории с обязательными префиксами geoip: или geosite:. Совместимое имя: UseTags. Дополняет выборку, не создаёт правил маршрутизации. При выключенном вырезании не действует. |
Сохраняется объединение категорий из JSON подписки (маршрутизация и DNS), применяемого профиля маршрутизации, активных пользовательских исключений и useTags. Для geosite:google@cn сохраняется категория целиком с атрибутами; фильтрацию по атрибуту выполняет Xray. Учитываются также стандартные ссылки ext:geoip.dat:tag, ext-ip:geoip.dat:tag и ext:geosite.dat:tag.
Готовый комплект привязан к подписке. Переключение её серверов использует этот комплект без повторного вырезания или разбора геобаз. При смене подписки используется её сохранённый комплект. Изменение содержимого исходных баз или набора категорий создаёт новый комплект. Учитываются все конфиги, сохранённые в подписке: один комплект подходит всем её серверам.
Полные оригиналы кэшируются по URL и общие для подписок с одинаковыми адресами баз. ETag и SHA-256 оригиналов хранятся отдельно от сокращённых файлов. Свежий кэш оригинала используется 24 часа. При проверке обновления сохранённого оригинала соседний файл .dat.sha256 (64 шестнадцатеричных символа), если он доступен, позволяет не скачивать неизменившееся содержимое. Иначе используется ETag/HTTP 304 при наличии. Вырезание не заменяет оригинал или его HTTP-метаданные. Если сервер не отдаёт ни хэш, ни ETag, проверка свежести может потребовать повторного скачивания оригинала.
Если провайдерская база не загрузилась или не содержит категорию из правил, проверяется встроенная. Если нужных категорий нет и там, предлагается подключение без геоправил с согласия пользователя. Отсутствующая категория только из useTags пропускается с диагностическим предупреждением. При ошибке самого вырезания используются проверенные полные базы, если они содержат все обязательные категории.