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

Метаданные подписки

Ответ эндпоинта подписки — это тело (список серверов) плюс метаданные (брендинг, срок действия, обновления). Метаданные можно передать двумя способами, и клиент читает оба.

A. HTTP-заголовок ответа

Profile-Title: My VPN

B. Строка-комментарий в теле

#profile-title: My VPN

★ Приоритет — у HTTP-заголовка. Строка #key: value в теле используется, только если одноимённого заголовка нет. Это позволяет отдавать метаданные тем, кто не может задавать заголовки (статический хостинг, файлы в Telegram и т.п.).

⇄ Значения вкл/выкл. Любой заголовок, который включает или выключает функцию, принимает набор эквивалентов без учёта регистра: 1 / on / true / yes означают включено, а 0 / off / false / no — выключено.

⚠ Функции обхода: ваша настройка важнее пользовательской. Для всех четырёх заголовков обхода — s-fragment, s-dns, s-noise, s-resolve — присланное вами значение важнее собственного переключателя пользователя, включая off. Переключатель пользователя работает только тогда, когда вы не прислали ничего. Так сделано намеренно: эти параметры обязаны совпадать с тем, что ожидает ваш сервер, и самостоятельное включение пользователем просто разорвало бы соединение.

Живые обновления подписки

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

Синтаксис строки в теле

  • Начинается с # , затем ключ [A-Za-z0-9-]+ , затем значение.

  • Двоеточие необязательно — #profile-title My VPN тоже сработает.

  • Ключ регистронезависим.

  • Первое вхождение ключа побеждает, повторы игнорируются.

  • Тело сканируется и как есть, и после полного base64-декодирования — работает и для текста, и для одного base64-блоба (обычный формат vless-ссылок).

  • Тело, начинающееся с [Interface], считается .conf WireGuard / AmneziaWG до любой проверки на JSON или base64 (Android 449, iOS / macOS / tvOS 331, desktop 218 и новее).

  • Из тела читаются только ключи из таблицы ниже; строки с другими ключами игнорируются.

base64 для не-ASCII (например, кириллицы)

Текстовые поля можно закодировать, добавив префикс base64: — работает и в заголовке, и в теле. Применимо к profile-title, s-title, announce, sub-expire-button-text. #profile-title: base64:0JzQvtC5IFZQTg== → «Мой VPN»

Ключи

Каждый ключ работает и как заголовок Ключ: значение, и как строка тела #ключ: значение. Все — бесплатно.

Брендинг и ссылки

Ключ (заголовок / #тело) Назначение
profile-title / s-title🔒 Заголовок подписки (текст или base64:) Без profile-title/s-title подписка показывается в панели «Свои серверы» как обычные серверы, без шапки провайдера (Android 469+, Apple 2.0.17 (344)+, Windows/Linux 238+).
s-sitename🔒 Название провайдера/сайта
s-siteurl🔒 URL сайта провайдера
s-tgbot / x-tgbot🔒 Ссылка на Telegram-бот/канал
support-url🔒 Ссылка на поддержку
announce🔒 Текст баннера-объявления (текст или base64:), до 5 строк — длиннее обрезается многоточием
announce-url🔒 Делает объявление кликабельным — открывает этот URL
support-email🔒 Email поддержки — добавляет на карточку подписки кнопку «Написать в поддержку»
profile-web-page-url🔒 Веб-страница провайдера

Статус подписки и продление

Ключ (заголовок / #тело) Назначение
subscription-userinfo🔒 upload=…; download=…; total=…; expire=… (квота + epoch окончания)
sub-expire / s-showexpire🔒 Показывать состояние срока действия
sub-expire-button-text🔒 Подпись кнопки продления (текст или base64:)
sub-expire-button-link-site🔒 Кнопка продления → сайт
sub-expire-button-link-tg🔒 Кнопка продления → Telegram
notification-subs-expire🔒 1 — слать уведомления «подписка истекает через N дней», начиная за 3 дня до окончания. Независим от sub-expire / s-showexpire. Отсутствует = без уведомлений. Также как #notification-subs-expire: 1 в теле.

Обновление и данные

Ключ (заголовок / #тело) Назначение
profile-update-interval🔒 Интервал авто-обновления подписки (часы)
fallback-url🔒 Резервный URL подписки
geoipurl / geositeurl🔒 Источники баз GeoIP / GeoSite (раздел 07)
sort-order🔒 Порядок серверов в списке: ping (сначала быстрые, по замеренной задержке — без замера уходят в конец), name (по алфавиту) или none (как пришли, по умолчанию). Неизвестные значения считаются как none.

Миграция и доступ

Ключ (заголовок / #тело) Назначение
new-url🔒 Полностью заменить URL подписки (миграция) — см. ниже
new-domain🔒 Заменить только хост URL: host или host:port (безопасно для персональных ссылок). Можно задать из кабинета провайдера, не трогая панель — см. раздел 05b
fallback-domains🔒 Запасные хосты через запятую. Если хост подписки перестал отвечать, приложение пробует их по порядку, сохраняя путь и токен каждого пользователя. В отличие от new-domain ничего не меняется навсегда — это выход, когда основной адрес заблокирован, а не переезд. Задаётся из кабинета провайдера или отправляется вами (раздел 05b)
hide-settings🔒 Скрывает от пользователя URL подписки и её конфиг — подробности в разделе 04b. 1 скрывает, 0 показывает; отсутствие заголовка ничего не меняет.

Идентификация

Ключ (заголовок / #тело) Назначение
providerid / s-providerid🔒 Provider ID (раздел 05). s- вариант предпочтителен; также читается из URL ?providerid=
subid🔒 Стабильный идентификатор подписки — позволяет менять URL подписки без появления дубликатов у пользователей (раздел 05a)

Обход блокировок

Ключ (заголовок / #тело) Назначение
s-noise🔒 Шумовые пакеты перед хендшейком — мусорный трафик, отправляемый до соединения, чтобы DPI не распознал его начало. Работает вместе с s-fragment (общий выход). on / 1 — параметры по умолчанию (rand, пакет 50-100, задержка 10-20); off / 0 — выключить. Полная форма: type=rand;packet=50-100;delay=10-20 или позиционно rand,50-100,10-20; type — rand, str или hex. → подробно в разделе 08
s-resolve🔒 Резолв адреса сервера через DoH до подъёма туннеля — для сетей, где локальный DNS подменяет ответ для домена вашей ноды. on / 1, off / 0 или URL резолвера с необязательным bootstrap-IP. → подробно в разделе 08
s-reconnect-on-update Переподключение активного VPN при изменении подключённого сервера после обновления подписки. По умолчанию включено; заголовок переопределяет локальный переключатель; Provider ID не требуется.
s-reality-disable-pq Отключение ML-KEM в REALITY без смены fingerprint; HTTP/файл/URL; без активного Provider ID. Подробности: Обход блокировок.
s-fragment🔒 Фрагментация TLS ClientHello против DPI по SNI (полный справочник — раздел 08).
s-dns🔒 DNS-over-HTTPS внутри туннеля: on / 1 — встроенный резолвер по умолчанию (doh.ffmuc.net); URL — этот резолвер; off / 0 — выключить. → подробно в разделе 08

🔒 Ключи с замком работают только при активном провайдере — полный список.

Прокси для приложений (Android) — per-app-proxy-*

Управляйте экраном «Прокси для приложений» у пользователя прямо из подписки — ключи те же, что у Happ, так что одна панель обслуживает оба приложения. Только Android: у iOS/macOS/tvOS и десктопа маршрутизации по приложениям нет, эти ключи они игнорируют.

Ключ (заголовок / #тело) Назначение
per-app-proxy-enable🔒 1 / 0 — включить или выключить функцию. 1 без режима сохраняет текущий режим пользователя (bypass, если было выключено).
per-app-proxy-mode🔒 off · on — через туннель идут только перечисленные приложения · bypass — перечисленные идут мимо туннеля, всё остальное через него.
per-app-proxy-list🔒 Имена пакетов через запятую, добавляются к выбору пользователя. Ещё не установленные приложения сохраняются и подхватываются при первом подключении после их появления.
per-app-proxy-list-set🔒 Тот же формат, но заменяет выбор целиком.
per-app-proxy-list-invert🔒 Тот же формат; выбирает все установленные приложения, кроме перечисленных.

Разделители в списке — , ; или пробел, допускается префикс base64:. Набор значений применяется один раз: приложение запоминает, что применило, поэтому пользователь может править выбор вручную, и плановое обновление подписки его не откатит — чтобы задать новый выбор, пришлите другие значения. Изменения вступают в силу при следующем подключении (Android фиксирует набор приложений при поднятии туннеля).

per-app-proxy-enable: 1
per-app-proxy-mode: bypass
per-app-proxy-list: ru.sberbankmobile,ru.alfabank.mobile.android,com.idamob.tinkoff.android,ru.yandex.yandexmaps

Баннер провайдера — banner-*

Баннер на карточке подписки, показывается только при активном provider id (совместимо с INCY). Цвета — только #RRGGBB; всё остальное игнорируется, и берётся цвет темы приложения. Цвет надписи на кнопке подбирается автоматически по яркости кнопки, поэтому остаётся читаемым и на светлом, и на тёмном фоне.

Заголовок Значение и назначение
banner-text🔒 текст или base64: — текст баннера, до 5 строк
banner-button-text🔒 текст или base64:, до 25 символов — надпись на кнопке
banner-button-url🔒 URL или deep-link — куда ведёт кнопка
banner-bg-color🔒 #RRGGBB — фон баннера
banner-button-color🔒 #RRGGBB — фон кнопки

Параметры обхода — сводка

Сводка по четырём заголовкам обхода; подробный разбор каждого — в разделе 08.

Заголовок Значения
s-reality-disable-pq true / 1 / on либо false / 0 / off; по умолчанию наследует клиентскую настройку (выключена).
s-fragment on | off | packets=<tlshello\|1-3\|all>;length=<min-max>;interval=<min-max>[;maxsplit=<n>] · по умолчанию, если прислано только on: packets=tlshello;length=50-100;interval=10-20
s-noise (s-noises) on | off | type=<rand\|str\|hex>;packet=<value>;delay=<min-max> · по умолчанию, если прислано только on: type=rand;packet=50-100;delay=10-20
s-resolve on | off | <doh-url>[;ip=<bootstrap>][, …] · по умолчанию, если прислано только on: встроенный резолвер
s-dns on | off | <doh-url> · по умолчанию, если прислано только on: встроенный резолвер (doh.ffmuc.net)

Совместимость с именами заголовков INCY / Happ

Если вы уже настраиваете другой клиент, приложение понимает и его отдельные ключи и сворачивает их в эквивалентную настройку. Наш заголовок s-* всегда важнее — ключи ниже читаются только когда соответствующего s-* нет. *-enable: 0 — явное выключение.

Их ключи Соответствует
fragmentation-enable · fragmentation-packets · fragmentation-length · fragmentation-interval · fragmentation-maxsplit s-fragment
noises-enable · noises-type / noises-packet-type · noises-packet · noises-delay · noises-rand s-noise
server-address-resolve-enable · server-address-resolve-dns-domain · server-address-resolve-dns-ip s-resolve

noises-rand: <n> превращается в type=rand;packet=<n>. noises-rand-range не поддерживается.

Откуда может прийти значение и что важнее: 1) HTTP-заголовок ответа — высший приоритет; 2) строка #ключ: значение в теле (а для команд маршрутизации — строка *://routing/… без префикса) — используется, если заголовка нет; 3) собственная настройка приложения — если провайдер ничего не присылает. Для четырёх заголовков обхода значение провайдера важнее переключателя пользователя, включая off.

Текстовые значения принимаются как обычный UTF-8 или с префиксом base64:; любое значение заголовка может прийти и зашифрованным как crypt1/.

Миграция URL: new-url / new-domain

Перенацеливают сохранённый URL подписки — провайдер может переехать, а пользователю не нужно добавлять подписку заново.

  • new-url — заменяет URL целиком (любой формат), один и тот же для всех подписок.

  • new-domain — меняет только хост (схема, порт, путь, query сохраняются); каждая подписка хранит свой путь/токен — безопасно для персональных URL. Если присланы оба — побеждает new-url.

Применяется, только если в том же ответе есть providerid И аккаунт этого провайдера активен. Мигрируются все локальные подписки с этим providerId. Флаг locked сохраняется: подписка из crypt1/addhw остаётся заблокированной на новом URL.

Алиасы для совместимости (Happ / INCY)

Заголовки, которые используют другие клиенты, читаются как их аналоги в SMProxy, когда заголовка SMProxy нет — панель, написанная под Happ или INCY, работает без изменений. Алиасы принимаются и в HTTP-заголовках, и в теле подписки в форме #key: value.

Шлёт другой клиент Читается как
subscription-name profile-title — запасное имя INCY
content-disposition: attachment; filename="x.txt" profile-title — имя файла без .txt / .yaml / .yml; самый низкий приоритет
homepage profile-web-page-url — INCY
hide-url hide-settings — 1/true скрывает, 0/false открывает
sub-info-text banner-text — Happ «расширенные объявления»; 0 = баннера нет
sub-info-button-text / sub-info-button-link banner-button-text / banner-button-url
sub-info-color (red · blue · green) banner-bg-color — переводится в hex-цвет
sub-expire-button-link sub-expire-button-link-tg для t.me / tg://, иначе sub-expire-button-link-site — одиночная форма Happ
routing-enable: 0 routing: routing/off — Happ «выключить маршрутизацию»
subscription-userinfo: 0 заголовка нет — INCY «скрыть блок трафика»
expire=<миллисекунды> в subscription-userinfo секунды — значения больше 32 000 000 000 считаются миллисекундами

При наличии обоих заголовков побеждает заголовок SMProxy.

Переподключение после обновления — s-reconnect-on-update

С Apple 3.0.2 (385), Android 3.0.1 (514), Windows/Linux 2.0.2 (267). Активный Provider ID не требуется. В настройках приложения функция включена по умолчанию. Явное значение заголовка имеет приоритет над локальным переключателем для этой подписки. Если значение отсутствует или некорректно, используется локальная настройка.

Допустимы true, false, 1, 0, on, off без учёта регистра, включая строки с логическим значением в кавычках. Поддерживается шифрование значения crypt1/. Приоритет: HTTP-заголовок → комментарий в теле → параметр URL подписки.

s-reconnect-on-update: true

Комментарий в теле:

#s-reconnect-on-update: false

Параметр URL:

https://panel.example.com/subscription?s-reconnect-on-update=true

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