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

Управление приложением

Параметры для управления поведением приложения через HTTP-заголовки подписки и строки в теле ответа.

Способы передачи

Для параметров с поддержкой обоих источников доступны два способа передачи. Исключения, работающие только через HTTP-заголовки (например, перенос URL и баннеры), указаны в соответствующих разделах:

1. HTTP-заголовок:

HTTP/2 200
profile-title: Мой VPN
support-url: https://t.me/support

2. Строка в теле подписки (комментарий с #):

#profile-title: Мой VPN
#support-url: https://t.me/support
#profile-update-interval: 6
#announce: Текст объявления
vless://...

Приоритет: HTTP-заголовки имеют приоритет. Строки в теле подписки используются как fallback, когда соответствующий заголовок отсутствует. Это особенно полезно при раздаче подписок через статические файлы (nginx), где нет возможности задать кастомные HTTP-заголовки.


Стандартные параметры

Имя подписки

Название профиля подписки. Максимум 25 символов. Можно передавать как текст или base64 (UTF-8).

Заголовок:

profile-title: Мой VPN

В теле подписки:

#profile-title: Мой VPN

Base64 с описанием (первая строка — имя, остальные — описание):

profile-title: base64:0JzQvtC5IFZQTgrQlNC+0LHRgNC+INC/0L7QttCw0LvQvtCy0LDRgtGM

Альтернативные заголовки (fallback, если profile-title отсутствует): - subscription-name — альтернативное имя подписки - content-disposition — имя файла из заголовка (расширения .txt, .yaml, .yml удаляются автоматически)

Описание подписки

Отдельный заголовок для описания, если оно не включено в profile-title:

profile-description: Быстрые серверы в Европе

Поддерживает base64: profile-description: base64:...

Интервал обновления подписки

Интервал автоматического обновления подписки в часах. Значение должно быть кратно одному часу.

Заголовок:

profile-update-interval: 6

В теле подписки:

#profile-update-interval: 6

Интервал обновления геофайлов

routing-geo-update-interval — расширение INCY для любого провайдера, передающего профиль маршрутизации с подпиской; Premium не требуется. Оно обновляет geoip.dat и geosite.dat независимо от profile-update-interval и обновления источника Autorouting.

profile-update-interval: 1
routing-geo-update-interval: 168

В этом примере подписка обновляется каждый час, геофайлы проверяются раз в 7 дней. Эквивалент в теле — #routing-geo-update-interval: 168, в том числе внутри base64-кодированной текстовой подписки. Принимается целое число часов 0–8760. Некорректные значения игнорируются; корректный заголовок имеет приоритет над телом. Отсутствие или 0 означает отсутствие расписания провайдера. Явно выбранный пользователем интервал или «Не обновлять» имеет приоритет над провайдером.

Интервалы в днях, безопасное скачивание и повторы описаны в разделе автообновления геофайлов.

Статус подписки

Информация о балансе, объёме использованного трафика и сроке действия подписки. Поля разделяются точкой с запятой.

subscription-userinfo: upload=0;download=1073741824;total=10737418240;expire=1700000000
Поле Описание
upload Исходящий трафик (байт)
download Входящий трафик (байт)
total Лимит трафика (байт)
expire Дата истечения (Unix timestamp, секунды)

Если expire > 32 000 000 000 — значение интерпретируется как миллисекунды и конвертируется в секунды.

Ссылка на поддержку

Кнопка перехода на страницу поддержки. Если ссылка ведёт в Telegram — отображается иконка Telegram.

Заголовок:

support-url: https://t.me/your_support_bot

В теле подписки:

#support-url: https://t.me/your_support_bot

Ссылка на сайт

Кнопка перехода на сайт подписки.

Заголовок:

profile-web-page-url: https://your-site.com

В теле подписки:

#profile-web-page-url: https://your-site.com

Альтернативный заголовок: homepage

Объявление

Текстовое объявление (до 200 символов). Можно передавать как текст или base64.

Заголовок:

announce: Плановое обслуживание 15 марта с 03:00 до 05:00 MSK

Base64:

announce: base64:0J/Qu9Cw0L3QvtCy0L7QtSDQvtCx0YHQu9GD0LbQuNCy0LDQvdC40LU=

URL объявления (ссылка, не текст):

announce-url: https://example.com/news

В теле подписки:

#announce: Плановое обслуживание 15 марта
#announce-url: https://example.com/news
vless://uuid@server:443#Server

Сводная таблица параметров

Заголовок Альтернативы Формат Body (#) Описание
new-domain — домен — Перенос только этой подписки: заменить хост; только HTTP
new-url — HTTPS URL — Перенос только этой подписки: заменить весь URL; только HTTP
profile-title subscription-name, content-disposition текст / base64:... ✅ Имя подписки
profile-description — текст / base64:... — Описание подписки
profile-update-interval — число (часы) ✅ Интервал обновления
routing-geo-update-interval — целое число часов (0–8760) ✅ Автообновление геофайлов профиля; 0 — отключено
subscription-userinfo — key=value;... — Статистика трафика
sort-order — none / ping / name — Порядок сортировки серверов (на iOS/Android — для этой подписки; на Desktop — глобально)
support-url — URL ✅ Ссылка на поддержку
support-email — email ✅ Email поддержки — показывает кнопку «Email» в карточке подписки (запасной контакт). Без заголовка кнопка не показывается
profile-web-page-url homepage URL ✅ Ссылка на сайт
announce — текст / base64:... ✅ Текст объявления
announce-url — URL ✅ Ссылка на объявление
autorouting — URL ✅ Автообновляемый профиль маршрутизации
routing — base64 / ссылка / off ✅ Профиль маршрутизации; значение off отключает встроенную маршрутизацию
premium-url — URL — Ссылка «Премиум» (в карточке подписки, см. ниже)
banner-text — текст / base64:... — Текст баннера (перебивает панель, см. ниже)
banner-button-text — текст / base64:... — Текст кнопки баннера
banner-button-url banner-url URL — Ссылка кнопки баннера
banner-bg-color — hex (#RRGGBB) — Цвет фона баннера
banner-button-color — hex (#RRGGBB) — Цвет кнопки баннера
expiry-banner-text — текст / base64:... — Баннер об истечении: текст баннера
expiry-banner-button-text — текст / base64:... — Баннер об истечении: подпись кнопки
expiry-banner-button-url expiry-banner-url URL — Баннер об истечении: ссылка кнопки
expiry-banner-bg-color — hex #RRGGBB — Баннер об истечении: фон баннера
expiry-banner-button-color — hex #RRGGBB — Баннер об истечении: фон кнопки
expiry-banner-text-color — hex #RRGGBB — Баннер об истечении: цвет текста баннера
expiry-banner-button-text-color — hex #RRGGBB — Баннер об истечении: цвет подписи кнопки
hide-url — 1 / 0 / true / false ✅ Скрыть URL подписки от Share/Copy/QR/backup (см. ниже)
hide-check — 1 / 0 / true / false ✅¹ Скрыть кнопку «Проверить» на главном экране (см. ниже)
per-app-proxy-enable — 1 / 0 — Включить per-app режим (только Android)
per-app-proxy-mode — bypass / proxy — Режим per-app
per-app-proxy-list — CSV / URL — Список package names
fragmentation-enable — 1 / 0 — TCP-фрагментация
fragmentation-length — min-max — Диапазон длины фрагмента
fragmentation-interval — min-max — Диапазон задержки между фрагментами
fragmentation-packets — tlshello / 1-3 / all — На какие пакеты применять
noises-enable — 1 / 0 — Отправка шумовых пакетов до handshake
noises-type — rand / str / hex — Тип шумового контента
noises-packet — строка — Payload шума (формат зависит от type)
noises-delay — min-max мс — Диапазон задержки между шумами
server-address-resolve-enable — 1 / 0 — Предварительный DNS-резолв адреса сервера через DoH
server-address-resolve-dns-domain — URL — URL DoH-сервера
server-address-resolve-dns-ip — IP — IP DoH-сервера (bootstrap)
no-limit-enabled — 1 / 0 — (iOS) Включить память-экономный режим Network Extension — держит фоновый процесс под лимитом iOS 50 МБ, чтобы xhttp-транспорт не убивался системой (VPN не отваливается). Только включает (не выключает ручной выбор пользователя)

Все заголовки регистронезависимы (profile-title = Profile-Title).

Параметры из тела подписки (body) используются как fallback — HTTP-заголовки всегда имеют приоритет.

Все заголовки со значениями поддерживают префикс base64: для передачи UTF-8 данных без проблем с Latin-1 / non-ASCII (применяется к announce, profile-title, profile-description, subscription-name, per-app-proxy-list, banner-text, banner-button-text).

Android и Desktop дополнительно понимают gzip-цепочки в значениях (base64:gzip:, gzip:base64:, gzip:) — полезно для длинных per-app-proxy-list. iOS поддерживает только base64: (без gzip).

¹ hide-check как HTTP-заголовок и через тело подписки (#hide-check:) работает так же, как hide-url, но маркер в теле #hide-check: читают только Android и iOS — Desktop применяет hide-check только через заголовок и Premium-конфиг.

⚠️ Кириллица в баннере. HTTP-заголовки не передают non-ASCII напрямую. Чтобы текст баннера или кнопки был на русском, кодируйте его в base64: banner-text: base64:<base64(UTF-8)> и banner-button-text: base64:<base64(UTF-8)>.


Маршрутизация

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

Статический профиль в base64. Подробнее — routing.md.

routing: ://routing/onadd/ewog...base64...

Значение off (или ссылка ://routing/off) полностью отключает встроенную маршрутизацию:

routing: off

Автообновляемый профиль маршрутизации

URL-источник профиля маршрутизации с периодическим обновлением. Подробнее — autorouting.md.

autorouting: incy://autorouting/onadd/https://raw.githubusercontent.com/user/repo/main/profile.json

Приоритет источников маршрутизации

Если профиль маршрутизации указан в нескольких местах, используется первый найденный по приоритету:

Приоритет Источник
1 (высший) Заголовок autorouting
2 Body — строка с URL (://autorouting/onadd/, ://autorouting/add/)
3 Заголовок routing
4 (низший) Body — строка с base64 (://routing/onadd/, ://routing/add/, ://routing/)

Важно: только ://autorouting/ устанавливает sourceURL и включает автообновление. Строки ://routing/onadd/{url} в теле подписки импортируют профиль одноразово, без привязки к источнику.


Описание сервера

Дополнительная подпись, отображаемая под именем сервера (максимум 30 символов). Добавляется после title через разделитель ?:

vless://uuid@server:443#Сервер1?serverDescription=base64-text

Per-app proxy (управление из подписки — только Android)

Android VpnService позволяет пропускать через VPN только выбранные приложения либо наоборот — исключать их из туннеля. Провайдер может форсировать этот режим из подписки через три заголовка (только Android). На Desktop сама фича есть, но управляется пользователем — см. подраздел ниже.

per-app-proxy-enable: 1
per-app-proxy-mode: bypass
per-app-proxy-list: com.android.chrome,org.telegram.messenger

Параметры

Заголовок Значение Описание
per-app-proxy-enable 1 / 0 Включить режим per-app
per-app-proxy-mode bypass | proxy bypass — указанные приложения обходят VPN. proxy — только они идут через VPN
per-app-proxy-list CSV или URL Список package names через запятую, перенос строки, или URL до текстового файла

Формат списка

Inline (CSV или строчный):

per-app-proxy-list: com.android.chrome,org.telegram.messenger,com.google.android.youtube

Base64 (для длинных списков):

per-app-proxy-list: base64:Y29tLmFuZHJvaWQuY2hyb21lCm9yZy50ZWxlZ3JhbS5tZXNzZW5nZXI=

Удалённый URL:

per-app-proxy-list: https://example.com/myapps.txt

Файл по URL — обычный plain text с package names по одному на строку или через запятую. Клиент скачивает его при применении подписки и при каждом обновлении.

Поведение

  • Если ни одно из трёх полей не задано — настройки пользователя в приложении не переопределяются.
  • Если per-app-proxy-enable задан в 0 — per-app режим выключается, даже если пользователь его включил локально.
  • Эти заголовки читает только Android (список — по package names).

Per-app на Desktop (Windows / macOS / Linux)

Десктоп-клиент тоже поддерживает per-app split-tunnel, но не через эти заголовки — а как пользовательскую настройку в приложении (экран «Split-tunneling»). Провайдер задать её из подписки не может; список формирует пользователь по именам/путям исполняемых файлов, а не по package names.

Под капотом Desktop не трогает системный firewall — правило пишется прямо в маршрутизацию xray через поле process (сопоставление по PID сокета, sniffing не нужен). Работает и для обычных, и для full-config подписок. Поддержаны Windows, macOS и Linux (движок ядра xray определяет процесс-инициатор: GetExtendedTcpTable на Windows, /proc на Linux, libproc на macOS).

Только в режиме TUN

Per-app на Desktop работает только когда включён режим TUN (xray сам держит TUN-устройство и видит процесс-инициатор). В режимах System Proxy / Proxy Only процесс не определяется, и per-app не применяется.


TCP-фрагментация

Перезаписывает глобальные настройки фрагментации пользователя для данной подписки.

fragmentation-enable: 1
fragmentation-packets: tlshello
fragmentation-length: 10-30
fragmentation-interval: 10-30
Заголовок Значение Описание
fragmentation-enable 1 / 0 Включить фрагментацию
fragmentation-packets tlshello | 1-3 | 1 | all На какие TCP-пакеты применять фрагментацию
fragmentation-length min-max Диапазон длины фрагмента в байтах
fragmentation-interval min-max Диапазон задержки между фрагментами в мс

Те же параметры доступны через Premium API как fragmentEnabled / fragmentPackets / fragmentLength / fragmentInterval — при Premium-подписке HTTP-заголовки игнорируются в пользу API-значений.


Шумовые пакеты (noises)

Отправка случайных UDP-пакетов перед VPN-handshake'ом для маскировки. Актуально в первую очередь для WireGuard и Hysteria2 в сетях с глубокой инспекцией трафика.

noises-enable: 1
noises-type: rand
noises-packet: 10-20
noises-delay: 10-50
Заголовок Значение Описание
noises-enable 1 / 0 Включить шумы
noises-type rand | str | hex Формат payload'а
noises-packet строка Содержимое шумового пакета; для rand — диапазон длины min-max
noises-delay min-max мс Диапазон задержки между шумовыми пакетами

Резолв адреса сервера через DoH

Bootstrap-резолв домена сервера через DNS-over-HTTPS до установки туннеля. Полезно когда провайдерский DNS подменяет адрес VPN-сервера.

server-address-resolve-enable: 1
server-address-resolve-dns-domain: https://common.dot.dns.yandex.net/dns-query
server-address-resolve-dns-ip: 77.88.8.8
Заголовок Значение Описание
server-address-resolve-enable 1 / 0 Включить DoH-резолв
server-address-resolve-dns-domain URL DoH endpoint (обычно /dns-query)
server-address-resolve-dns-ip IP IP DoH-сервера для bootstrap'а — используется до резолва его домена

Те же поля передаются через Premium API как serverAddressResolveEnable / serverAddressResolveDnsDomain / serverAddressResolveDnsIp.


Ссылка «Премиум»

Дополнительная кнопка в карточке подписки — ведёт на страницу покупки или личный кабинет провайдера.

premium-url: https://example.com/pricing
Заголовок Значение Описание
premium-url URL URL кнопки «Премиум» в карточке подписки

Если не задан — кнопка скрыта.


Баннер провайдера

Баннер — заметная плашка в карточке подписки на главном экране (текст + опциональная кнопка). Настраивается в премиум-панели, но текст и кнопку можно переопределить через заголовки подписки — удобно для динамических объявлений без захода в панель.

Условия показа

Баннер показывается, когда провайдер — премиум (активная премиум-подписка) и выполнено хотя бы одно из:

  1. пришёл заголовок banner-text в ответе подписки (динамический баннер — например, через Remnawave Response Rules, без захода в премиум-панель INCY); или
  2. баннер включён в премиум-панели (bannerEnabled).

То есть заголовок banner-text сам по себе включает баннер (при условии, что провайдер премиум) — заходить в панель и ставить bannerEnabled не обязательно. Если banner-text не пришёл, баннер показывается по старому пути (панельный bannerEnabled + текст из панели).

Слать баннер через заголовки могут только премиум-провайдеры: для не-премиум подписок banner-* заголовки игнорируются.

Приоритет: заголовок → панель

Каждое поле резолвится как заголовок ?? панель: если соответствующий banner-* заголовок пришёл в ответе подписки — берётся он, иначе — значение из панели.

banner-text: base64:0JDQutGG0LjRjyEg0KHQutC40LTQutCwIDUw0Js=
banner-button-text: Подробнее
banner-button-url: https://example.com/promo
banner-bg-color: #E53E3E
banner-button-color: #38A169
Заголовок Формат Переопределяет (панель)
banner-text текст / base64:... bannerText
banner-button-text текст / base64:... bannerButtonText
banner-button-url URL bannerButtonUrl
banner-bg-color hex #RRGGBB bannerBgColor
banner-button-color hex #RRGGBB bannerButtonColor

Текст (banner-text) и текст кнопки (banner-button-text) поддерживают base64: — для кириллицы/UTF-8 это обязательно (см. предупреждение выше).

URL кнопки резолвится как premium-url → banner-button-url → панель: заголовок premium-url имеет наивысший приоритет (исторически).

Если после резолва текст баннера пуст — баннер не показывается.

Поддерживается на iOS, Android и Desktop.


Баннер истекающей и истёкшей подписки

Premium-провайдер может переопределить этот баннер HTTP-заголовками ответа подписки. Они независимы от обычного banner-*; premium-url не перебивает ссылку кнопки истечения. Строки # в теле подписки для этих полей не поддерживаются.

Заголовок Формат Поле панели
expiry-banner-text текст / base64:... expiryBannerText
expiry-banner-button-text текст / base64:... expiryBannerButtonText
expiry-banner-button-url URL expiryBannerButtonUrl
expiry-banner-bg-color hex #RRGGBB expiryBannerBgColor
expiry-banner-button-color hex #RRGGBB expiryBannerButtonColor
expiry-banner-text-color hex #RRGGBB expiryBannerTextColor
expiry-banner-button-text-color hex #RRGGBB expiryBannerButtonTextColor
expiry-banner-text: base64:0J/RgNC+0LTQu9C40YLQtSDQv9C+0LTQv9C40YHQutGD
expiry-banner-button-text: base64:0J/RgNC+0LTQu9C40YLRjA==
expiry-banner-button-url: https://example.com/renew
expiry-banner-bg-color: #082C22
expiry-banner-button-color: #29C896
expiry-banner-text-color: #FFFFFF
expiry-banner-button-text-color: #062018

expiry-banner-url — короткий вариант expiry-banner-button-url. Если переданы оба, приоритет у полного имени. Для обычного баннера также поддерживается banner-url как вариант banner-button-url. Кириллицу и другой не-ASCII текст передавайте через base64:.

Условия показа и приоритет

  • У провайдера должен быть активен Premium. Для непремиум-подписок эти баннеры не показываются.
  • Непустой expiry-banner-text включает баннер без expiryBannerEnabled в панели. Одна ссылка или цвет только переопределяют уже настроенный баннер.
  • Клиент использует subscription-userinfo → expire: баннер появляется за 3 дня до истечения или после него. Без известной даты истечения он не включается. Заголовок не меняет срок действия подписки.
  • Каждое переданное непустое поле имеет приоритет над панелью. Удаление заголовка или пустое значение возвращает значение панели при следующем успешном обновлении подписки. Переопределение сохраняется после перезапуска приложения.
  • Обычный баннер объявления/технических работ сохраняет приоритет над обычным баннером истечения. Если в панели настроено отдельное напоминание о продлении после истечения, expiry-banner-* переопределяет также его текст, кнопку и цвета; расписание его показа не меняется.
  • Явный текст из заголовка сохраняется после истечения. Без него используется прежний текст из панели или стандартный текст об истечении. Выбирайте формулировку, подходящую до и после срока, либо меняйте ответ динамически.

Поддерживается на iOS, Android и Desktop; на TV ссылка действия отображается QR-кодом. Чтобы клиент получал новые значения и после истечения, провайдер должен продолжать возвращать заголовки и дату истечения в ответе подписки.


Скрытие URL подписки от пользователя

Параметр hide-url блокирует «утечку» URL подписки за пределы устройства: при включении приложение не показывает и не разрешает экспортировать URL ни через Share, ни через Copy URL, ни через QR-код, ни в резервные копии. Имя подписки, серверы и весь остальной UX работают как обычно — скрывается только сам URL.

Заголовок:

hide-url: 1

В теле подписки:

#hide-url: 1
vless://...

Premium API (JSON):

{
  "hide_url": true,
  "settings": { ... }
}

Принимаемые значения

Что прислано Поведение
1, true, yes (без учёта регистра) URL скрыт
0, false, no, пустая строка URL открыт (по умолчанию)
Любое другое значение в заголовке Игнорируется, URL открыт

Приоритет источников

  1. HTTP-заголовок hide-url имеет наивысший приоритет
  2. Строка #hide-url: в теле подписки используется как fallback при отсутствии заголовка
  3. Поле hide_url в Premium API JSON — независимый источник, применяется параллельно

Если хотя бы один источник говорит «скрыть» — URL скрывается. Чтобы снять скрытие, отключите его во всех источниках (либо обновите подписку с обновлённым значением).

Что блокируется

  • Кнопка Share в карточке подписки
  • Кнопка Copy URL
  • Показ QR-кода подписки
  • Включение подписки в резервную копию (даже зашифрованную паролем)
  • Кнопка экспорта в редакторе серверов подписки

Что НЕ блокируется

  • Просмотр и редактирование настроек серверов внутри подписки (на устройствах с admin HWID — см. admin-hwids.md)
  • Подключение к серверам, ping, traffic stats, обновление подписки
  • Резервные копии отдельных серверов (если они импортированы вручную, вне этой подписки)

Правила для Premium-провайдеров

Premium Admin HWID hide-url Видит URL URL в backup
нет — нет да да
нет — да нет нет
да да нет да да
да да да да нет (admin видит, но экспортировать через backup нельзя)
да нет — нет нет

Без hide-url обычные (не-premium) подписки всегда экспортируются и копируются — hide-url это единственный способ запретить это у не-premium.


Скрытие кнопки «Проверить»

Параметр hide-check убирает с главного экрана кнопку «Проверить» (проверка соединения при активном VPN). Работает по той же схеме, что и hide-url: tri-state (задано «скрыть» / задано «показать» / не задано → fallback). По умолчанию кнопка показывается.

Полезно провайдерам, чей сервер отвечает на check-запрос нестандартно (или блокирует его), из-за чего кнопка вводит пользователя в заблуждение.

Заголовок:

hide-check: 1

В теле подписки (только Android и iOS — Desktop игнорирует маркер в теле):

#hide-check: 1
vless://...

JSON-тело подписки:

{ "hide_check": true }

Premium API (JSON):

{
  "settings": { "hideCheck": true }
}

Принимаемые значения

Что прислано Поведение
1, true, yes (без учёта регистра) Кнопка «Проверить» скрыта
0, false, no, пустая строка Кнопка показана (по умолчанию)
Заголовок отсутствует Fallback на тело / Premium-конфиг, иначе показана

Приоритет источников

  1. HTTP-заголовок hide-check — наивысший приоритет
  2. Маркер #hide-check: в теле подписки — fallback (Android/iOS)
  3. Поле settings.hideCheck в Premium API — независимый источник

Платформы

Android, iOS и Desktop — все три скрывают кнопку. Отличие только в источнике «тело подписки»: маркер #hide-check: Desktop не читает (используйте заголовок или Premium-конфиг).


Настройки, которые подписка не может задать HTTP-заголовками

Эти параметры хранятся в Premium API конфигурации провайдера и применяются клиентом только если домен подписки принадлежит Premium-провайдеру. Включить их «через подписку без аккаунта в панели» нельзя — это by design.

Группа Что настраивается Ссылка
Lite Mode Упрощённый интерфейс, ссылки на бот/канал/поддержку + пресет-иконки premium-api.md § Lite Mode
Баннер подписки Текст + цвета + кнопка в карточке подписки premium-api.md § Баннер подписки
Кастомная тема Плоские цвета и градиенты для аккаунта / фона premium-api.md § Кастомная тема
Force-настройки forceConnectionStyle: классическая круглая кнопка vs compact-toggle premium-api.md § Принудительные настройки
Ping и сортировка defaultPingProtocol, defaultSortOrder, pingOnUpdate premium-api.md § Ping и сортировка
Domain fronting resolveAddress / hostHeader (связка SNI ↔ Host для маскировки) premium-api.md § Domain fronting и фрагментация
Админ-доступ по HWID adminHwids + auto-approve пушей admin-hwids.md
Push-уведомления Модерация, таргетинг, отмена provider-notifications.md

Фрагментация, шумы и DoH-резолв доступны через оба канала: HTTP-заголовками подписки (см. выше) или через Premium API. При Premium-подписке API-значения приоритетнее заголовков.

Перенос конкретной подписки на новый адрес

В HTTP-ответе с успешно разобранной подпиской можно передать один из заголовков:

new-domain: project-a.example.com

Заменяет только хост: https://old.example.com:8443/sub/token%2F?key=a+b#HWID → https://project-a.example.com:8443/sub/token%2F?key=a+b#HWID. Порт, путь, query-параметры и фрагмент сохраняются без декодирования токена. Значение — только имя домена, без схемы, порта и пути; для международных доменов используйте ASCII/Punycode.

Если меняются также путь или токен, передайте полный адрес:

new-url: https://project-b.example.com/new-path/token?key=a%2Bb

new-url заменяет весь URL, включая query и фрагмент. Если нужны прежние значения, включите их в новый адрес. При наличии обоих заголовков приоритет у new-url; некорректный new-url не переключает обработку на new-domain. Значения передаются обычным текстом, без base64:; #new-domain: и #new-url: в теле ответа не поддерживаются.

Перенос относится только к подписке, получившей этот ответ. Клиент сохраняет её ID, настройки и привязку маршрутизации; новая карточка не создаётся. Две подписки одного провайдера могут получить разные адреса — ни URL, ни решение о переносе не записываются в общий кэш провайдера. Текущее обновление использует полученный ответ, следующее обращается уже к новому URL.

Проверка перед переносом

  • Исходный URL, URL фактического ответа после редиректов/fallback и новый URL должны использовать HTTPS без логина/пароля в адресе.
  • Все три домена должны быть верифицированы, не удалены и принадлежать одному и тому же провайдеру в панели. Платный статус сам по себе не заменяет проверку домена.
  • Панель проверяет актуальные записи по SHA-256 хешам доменов. Путь, токен и полный URL подписки в проверку не передаются; подтверждение не кэшируется.
  • Заголовок с тем же доменом, некорректный адрес, конфликт с URL другой сохранённой подписки или изменение исходного URL пользователем во время запроса отменяют перенос. В частности, new-url с новым путём, но прежним доменом, игнорируется.
  • Если панель недоступна, не поддерживает проверку или отказывает в ней, прежний URL сохраняется; обычное обновление успешно полученной подписки продолжается. Пустой/ошибочный ответ подписки не переносит адрес.

Поддержка требует обновлённых клиентов Android/Android TV, iOS/tvOS и Desktop и обновлённой панели с проверкой /api/subscription/resolve?h=…&target=…&origin=…. Это отдельный механизм от общего переноса домена и fallbackHosts. Старый адрес должен ещё отвечать — либо ответ должен приходить через допустимый fallback. Уже полностью недоступный домен не сможет доставить заголовок клиенту.