---
date: 2026-08-22
tags:
  - tproxy
  - telegram
  - web-прокси
  - боты
  - mtproxy
  - выдача-доступа
aliases:
  - Как выдавать WEB-прокси из Telegram-бота
  - t.me/webproxy ссылка формат
  - WEB-прокси в своём боте
  - Раздача tproxy пользователям
  - Отзыв доступа WEB-прокси
  - Сколько профилей в profiles.json
  - Можно ли автоматизировать выдачу WEB-прокси
link: https://github.com/telegramdesktop/tproxy-server
---

# 🤖 Выдача WEB-прокси Telegram из своего бота

![[tproxy-bot-header.png|Обложка: выдача WEB-прокси из Telegram-бота — две строки на пользователя]]

> [!info] О чём заметка
> WEB-прокси — новый, четвёртый тип прокси в Telegram, где трафик едет внутри обычных HTTPS-запросов к вашему сайту. Что это такое и зачем нужно — в обзорной заметке [[tproxy/tproxy|WEB-прокси Telegram: трафик внутри обычного сайта]]; как поднять сервер — в [[tproxy/tproxy-server-setup|Установка tproxy-server]]. Здесь разбирается только один вопрос: как раздавать такой доступ пользователям из любого Telegram-бота, что для этого нужно хранить и почему автоматическая выдача упирается в неожиданное ограничение.

> [!warning] Насколько это проверено
> Разбор сделан по исходному коду серверной части `tproxy-server` (репозиторий `telegramdesktop/tproxy-server`, состояние на 22 августа 2026) и по коду Telegram Desktop. Автор прямо помечает проект как доказательство работоспособности (`proof-of-concept`) — экспериментальную реализацию, а не готовый продукт. Поля конфигурации и формат ссылки могут поменяться без сохранения совместимости. Практических историй массовой выдачи WEB-прокси через ботов на 22 августа 2026 года нет: клиентская поддержка ещё не дошла до стабильных сборок Telegram (подробности в [[tproxy/tproxy#Можно ли этим пользоваться прямо сейчас|обзорной заметке]]).

## TL;DR

1. Пользователю нужно передать **всего два значения** — имя хоста и 32-символьный шестнадцатеричный секрет. Это на порядок проще, чем выдача VLESS или WireGuard, где генерируются ключи и целые конфигурационные файлы.
2. Ссылка для передачи выглядит как `https://t.me/webproxy?server=proxy.example.com&secret=000102…`, есть и вариант `tg://webproxy` с теми же параметрами.
3. Секреты живут в файле `profiles.json` на сервере. Их количество ограничено параметром `max_profiles` — по умолчанию **32**.
4. Главная проблема автоматизации: **сервер не умеет перечитывать конфигурацию на лету**. Добавить профиль можно только перезапуском процесса, а перезапуск рвёт активные сессии всех остальных пользователей.
5. Отзыв доступа у одного человека возможен только сменой секрета его профиля. Поэтому «один профиль на пользователя» упирается в потолок из пункта 3, а «один общий секрет на всех» лишает возможности отключить конкретного человека.
6. Практичный вывод на август 2026: бот может раздавать заранее нарезанный пул профилей, но не выдавать доступ по-настоящему динамически.

## Что вообще нужно выдать пользователю

Сначала — чем WEB-прокси отличается от привычных схем выдачи. Когда бот выдаёт доступ к VLESS, он создаёт на сервере отдельную запись пользователя со своим идентификатором `UUID` — длинным уникальным номером, который сервер сверяет при каждом подключении. Когда бот выдаёт WireGuard, он генерирует пару ключей и собирает целый конфигурационный файл с адресами и маршрутами. В обоих случаях на сервере появляется физическая сущность, привязанная к конкретному человеку.

С WEB-прокси всё устроено иначе, и это устройство унаследовано от MTProxy. Пользователю передаются ровно два поля:

```text
Hostname: proxy.example.com
Secret:   000102030405060708090a0b0c0d0e0f
```

Имя хоста пишется без `https://`, без порта, без слеша и без параметров: тип прокси WEB жёстко подразумевает HTTPS и порт 443. Секрет — те же 32 шестнадцатеричных символа (16 байт), что и у обычного MTProxy; общепринята запись в нижнем регистре, её же требует установщик сервера. Никаких пользовательских ключей, сертификатов и профилей на стороне клиента не создаётся.

Проще говоря: доступ к WEB-прокси — это не персональная учётная запись, а знание пароля. Кто знает пару «домен + секрет», тот подключается; сервер не различает, один это человек или тысяча.

Из этой пары клиент сам, локально, вычисляет так называемый **bridge-capability** — производный «пропуск», который потом уходит в сетевой запрос вместо самого секрета. Считается он как `HMAC-SHA256`, где ключом служат байты секрета, а сообщением — строка `tdesktop-web-proxy-bridge-v1\n` вместе с именем хоста. HMAC — это способ получить из ключа и сообщения короткий отпечаток, который невозможно подделать, не зная ключа.

Практическое следствие этой формулы важнее самой криптографии: **пропуск привязан к домену**. Один и тот же секрет на двух разных доменах даёт два разных пропуска. Поэтому бот не может выдать пользователю «секрет вообще» — он всегда выдаёт пару. При переезде на другой домен ранее выданные ссылки ломаются просто потому, что в них записан старый домен. Сам секрет при этом не обесценивается: если тот же секрет прописан в профиле нового домена, достаточно перевыдать ссылку с новым адресом. Обратное тоже верно — один секрет на двух доменах технически допустим, но связывает обе площадки одним ключом.

## Формат ссылки и как её отдавать в чате

Готовая ссылка, которую бот отправляет пользователю сообщением:

```text
https://t.me/webproxy?server=proxy.example.com&secret=000102030405060708090a0b0c0d0e0f
```

Клиенты также принимают эквивалентную запись через внутреннюю схему приложения: `tg://webproxy?server=…&secret=…`. Это тот же самый набор параметров, просто ссылка открывается напрямую установленным клиентом, минуя веб-страницу.

> [!warning] Публичный t.me пока не знает этого маршрута
> В документации проекта прямо сказано: публичный фронтенд `t.me` маршрут `webproxy` пока не регистрирует. То есть ссылка вида `https://t.me/webproxy?...` на 22 августа 2026 года не обязательно откроется как прокси — при тестировании её нужно скармливать клиенту напрямую. Для бота это значит, что нельзя рассчитывать на привычный сценарий «пользователь нажал на ссылку, всё настроилось само»: пока разумнее отдавать и ссылку, и два поля отдельным текстом, чтобы человек мог ввести их руками.

Отдельная тонкость касается доменов с национальными буквами. Если ваш домен интернационализированный (например, кириллический), пользователю нужно публиковать его в форме `xn--…` — это ASCII-запись такого имени, называемая A-label. Причина техническая: разные версии библиотеки, на которой построен клиент, по-разному приводят некоторые символы к каноническому виду, из-за чего один и тот же домен на Windows и на Linux даёт **разные пропуски** и подключение молча не состоится — подробности в [[tproxy/tproxy-protocol|разборе протокола]]. Бот, который отдаёт домен в ASCII-форме, эту проблему обходит целиком.

## Где на сервере живут секреты

Все выданные секреты перечислены в одном файле; путь к нему задаётся обязательным полем настроек. В эталонной установке оператор правит `/etc/tproxy-server/profiles.json`, а служба читает его копию, которую systemd подкладывает во временный каталог при каждом старте. Структура файла проста:

```json
{
  "profiles": [
    {
      "name": "alpha",
      "secret": "0123456789abcdef0123456789abcdef",
      "backend": "127.0.0.1:2398",
      "carrier_mode": "https"
    },
    {
      "name": "beta",
      "secret": "fedcba9876543210fedcba9876543210",
      "backend": "127.0.0.1:2399",
      "carrier_mode": "websocket",
      "limits": {
        "max_sessions": 32,
        "max_streams": 512,
        "max_streams_per_session": 32
      }
    }
  ]
}
```

Каждая запись здесь называется **профилем** — это именованный набор «секрет плюс правила», под которым подключается группа пользователей. От этих полей зависит вся модель выдачи.

**`name`** — идентификатор от 1 до 64 символов, уникальный внутри файла. Он не виден пользователю и нужен серверу для учёта: по имени профиля считаются его сессии, подключения к бэкенду и ограничения скорости создания новых соединений.

**`secret`** — тот самый секрет, который получает пользователь. Принимается в шестнадцатеричном виде (32 или 34 символа) либо в кодировке base64url. Раскодироваться он обязан ровно в 16 или 17 байт, причём 17-байтовый вариант должен начинаться с байта `dd` — это унаследованный от MTProxy признак режима случайного дополнения. Два профиля не могут иметь секреты, дающие одинаковый пропуск: такой файл сервер не примет.

**`backend`** — адрес, куда реле отправляет извлечённый из кадров трафик этого профиля, то есть адрес работающего рядом MTProxy. Обязательно локальный числовой адрес вида `127.0.0.1:2398`; имена вроде `localhost` и внешние адреса отвергаются. Это осознанное ограничение архитектуры: реле физически не может отправить трафик наружу, куда попросит клиент, поэтому даже взломанное реле не превращается в открытый прокси для всего интернета.

**`carrier_mode`** — режим доставки, один из четырёх (`https`, `https-lanes`, `websocket`, `websocket-lanes`); по умолчанию `https`. Подробнее эти режимы разобраны в [[tproxy/tproxy-protocol|заметке про устройство протокола]]. Здесь важно другое: режим выбирается **на сервере, через профиль**, и клиент про него ничего не знает. Значит, бот может выдавать разным группам пользователей разные секреты и тем самым переключать им режим доставки — например, отдавать `websocket` тем, у кого сеть его пропускает, и консервативный `https` остальным. Со стороны пользователя это по-прежнему просто «другой секрет».

**`limits`** — необязательные квоты профиля: сколько одновременных сессий и потоков ему разрешено, с какой скоростью он может создавать новые, сколько данных копить. Пропущенное значение наследуется из общих настроек сервера (а два поля — предел потоков на сессию и число одновременных подключений к бэкенду — берут меньшее из общей настройки и собственного предела потоков профиля). Заданное значение может только **понизить** общий потолок, но не поднять; ноль означает «наследовать», поэтому обнулить лимит в профиле нельзя. Для бота это единственный встроенный механизм справедливого дележа: если один секрет раздали слишком широко, его квота не даст ему съесть весь сервер.

У файла не должно остаться ни одного бита прав для группы и для остальных — на практике это `0400`, но и `0600` проверку проходит. Сервер это проверяет и отказывается стартовать, если файл доступен группе или всем остальным. Исключение сделано для механизма systemd, который подкладывает секреты сервису во временный каталог; тогда допускаются права только на чтение.

## Главное ограничение: перезапуск ради каждого нового пользователя

**Сервер читает конфигурацию ровно один раз — при запуске.** Обработчика сигнала на перечитывание файлов в коде нет: процесс подписан только на сигналы завершения работы. Значит, добавление нового профиля в `profiles.json` само по себе не делает ничего — сервер продолжает работать со старым списком секретов, пока его не перезапустят.

А перезапуск, в свою очередь, не бесплатный. В документации это сказано прямо: рестарт реле **аннулирует все активные сессии**, и существующие соединения намеренно не восстанавливаются. Клиенты после этого пересоздают транспорт и подключаются заново, то есть у каждого активного пользователя в этот момент происходит обрыв связи на несколько секунд.

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

Отсюда следуют три работающие стратегии, и выбирать приходится осознанно.

Прежде чем их перечислять, важная деталь, которую легко упустить: **секрет должен знать не только реле, но и MTProxy за ним**. Реле передаёт байты рукопожатия прозрачно и в них не заглядывает, поэтому каждый выданный секрет обязан быть перечислен в аргументах запуска MTProxy. Профиль, добавленный только в файл профилей, даст странную картину: настройки вроде бы верные, а подключение не проходит. Хорошая новость в том, что перезапуск MTProxy, в отличие от перезапуска реле, активные сессии не рвёт — потоки просто переподключаются через живую сессию.

**Пул заранее нарезанных профилей.** Оператор один раз создаёт N профилей со случайными секретами (по умолчанию их не может быть больше 32 — это параметр `max_profiles`), прописывает те же секреты бэкенду MTProxy, запускает сервер и дальше просто раздаёт неиспользованные секреты из этого пула. Бот хранит соответствие «профиль → пользователь» в своей базе, а сервер о пользователях ничего не знает. Перезапуски не нужны вовсе, пока пул не исчерпан. Плата: потолок числа выданных секретов на один домен — по умолчанию 32, поднять его можно, но это правка файла настроек и, значит, тот же перезапуск, — и необходимость планировать пул заранее.

**Пакетное применение.** Бот копит заявки и применяет их окном — например, раз в сутки ночью, одним перезапуском. Пользователь получает доступ не мгновенно, а к утру. Для платного сервиса это обычно неприемлемо, для закрытого круга — вполне.

**Один общий секрет.** Все пользователи получают один и тот же секрет, как это давно делается с обычным MTProxy. Выдача становится мгновенной и бесплатной, перезапусков нет вообще. Плата — невозможность отключить одного человека.

## Отзыв доступа: то, чего в этой модели нет

Отзыв — обратная сторона «доступ это просто знание пароля». Раз сервер не различает пользователей внутри профиля, отобрать доступ у одного человека, не тронув остальных, невозможно. Единственный способ сделать секрет недействительным — изменить его в `profiles.json` и перезапустить реле. После этого доступ теряют **все**, кому этот секрет был выдан.

Для сравнения: в VLESS отзыв — это удаление конкретной записи пользователя на узле, и соседи ничего не замечают. Именно поэтому подписочные сервисы обычно строятся на VLESS, а не на MTProxy: без индивидуального отзыва невозможно закрыть доступ по окончании оплаченного периода.

Если индивидуальный отзыв нужен, остаётся схема «один профиль на пользователя»: тогда смена секрета в его профиле бьёт только по нему. Но эта схема сразу упирается во всё те же два ограничения — потолок числа профилей и перезапуск сервиса ради изменения. Плюс появляется третье, но здесь важно не преувеличить. Если всем профилям достаточно общей политики и маршрутизации, им хватит одного процесса MTProxy, которому просто перечислены все секреты. Отдельный процесс на своём порту (и своя строка в правилах файрвола) нужен только тем профилям, которым требуется собственная маршрутизация или раздельная статистика на стороне MTProxy: сам MTProxy ведёт учёт на уровне процесса, а не отдельного секрета.

> [!important] Честный вывод про биллинг
> На август 2026 WEB-прокси не годится для сервиса с оплатой по подписке и автоматическим отключением после окончания периода. Механизма, который делает это дёшево, в архитектуре нет: ни персональных учётных записей, ни горячего применения изменений, ни отзыва без ущерба соседям. Он хорошо подходит для другого — раздать рабочий доступ ограниченному кругу людей там, где остальные способы не проходят по сети.

## Что бот должен хранить у себя

Раз сервер не ведёт учёт пользователей, всё состояние ложится на бота. Минимальный набор полей на одну выдачу:

- имя хоста, с которого выдан доступ (оно же часть пропуска, поэтому обязательно сохранять вместе с секретом, а не подставлять «текущий домен» при показе);
- имя профиля на сервере — чтобы знать, какую запись править при отзыве;
- сам секрет в открытом виде: из работающего процесса реле его уже не достать (после старта в памяти остаётся только производный пропуск), но на сервере он продолжает лежать открытым текстом в файле профилей и в переменных окружения MTProxy. Хранить его у бота нужно не потому, что он безвозвратно теряется, а чтобы не ходить за ним на сервер и не зависеть от его переустановки;
- идентификатор пользователя Telegram и дата выдачи;
- режим доставки, если разным группам выдаются разные (иначе потом не понять, почему у части людей другое поведение).

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

## Сколько ботов и сколько доменов

Ещё одно ограничение, которое стоит заложить в архитектуру сразу: **один процесс реле обслуживает ровно одно имя хоста** — в настройках ровно одно поле для публичного домена (подробнее в [[tproxy/tproxy-server-setup|заметке про установку]]).

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

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

## Пошаговый план внедрения

- [ ] Поднять сервер по инструкции из [[tproxy/tproxy-server-setup|заметки про установку]] и убедиться, что обычный сайт на домене открывается, а служебные порты снаружи закрыты.
- [ ] Решить модель выдачи: пул профилей, пакетное применение или один общий секрет. От этого зависит вся остальная логика бота.
- [ ] Если выбран пул — сгенерировать секреты заранее (`openssl rand -hex 16` на каждый), вписать их в `profiles.json` одним разом и запустить сервер; после этого перезапуски не понадобятся.
- [ ] Завести в базе бота таблицу выдач с полями из раздела выше, обязательно включая домен и имя профиля.
- [ ] Научить бота отдавать пользователю и готовую ссылку, и два поля отдельным текстом — на случай, если ссылка `t.me/webproxy` в его клиенте не сработает.
- [ ] Заранее написать процедуру отзыва (смена секрета плюс перезапуск) и честно посчитать, скольких пользователей она затронет.
- [ ] Проверить, что в журналах бота и сервера не оседают ни секреты, ни ссылки с пропуском: адрес bridge-запроса содержит производный пропуск, и его попадание в логи равносильно утечке доступа.

## 📚 См. также

- [[tproxy/tproxy|WEB-прокси Telegram: трафик внутри обычного сайта]] — обзор нового типа прокси и ответ на вопрос, можно ли им пользоваться уже сейчас.
- [[tproxy/tproxy-server-setup|Установка tproxy-server: свой WEB-прокси на своём домене]] — развёртывание сервера от DNS-записи до проверки.
- [[tproxy/tproxy-protocol|Как устроен WEB-прокси изнутри]] — кадры, пропуск, режимы транспорта и лимиты.
- [[mtproxy/mtproxy|MTProxy и Telegram-транспорты — раздел]] — обычный MTProxy, от которого WEB-прокси унаследовал модель секретов.
- 🔗 [telegramdesktop/tproxy-server](https://github.com/telegramdesktop/tproxy-server) — исходники серверной части и вся официальная документация проекта.

---

> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование доступно в Forgejo: [исходник этой заметки](https://git.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/tproxy/tproxy-in-bot.md) · [скачать весь репозиторий одним zip-архивом](https://git.zapret.moe/zapretdiscordyoutube/todo/archive/main.zip).
