# 🧊 Как DPI «замораживает» VLESS+REALITY: разбор схемы ограничений (июнь 2026)
> [!quote] Первоисточник
> Этот разбор основан на статье **Петра Осетрова** (@hyperion_cs) на Хабре:
> 👉 **[«О схеме ограничений РКН в июне 2026-го» — habr.com/ru/articles/1044396](https://habr.com/ru/articles/1044396/)**
>
> Все числовые параметры (пороги, тайминги, списки фингерпринтов) взяты из этой статьи. Ниже — простое объяснение «на пальцах» плюс расширенные технические детали по uTLS и mux от себя.
> [!info] Дисклеймер
> Материал носит **исследовательский и образовательный** характер. Задача — понять, *по каким признакам* современный DPI отличает обходные средства от обычного трафика, и почему одной волшебной галочкой проблему больше не решить.
> [!warning] Важно про статус этих данных
> Описанная схема — это **результат реверс-инжиниринга**: автор сам называет её «Siberian» и добавил одноимённый чекер в dpi-checkers. Это наблюдения **одного исследователя**; сам автор оговаривает, что **параметры метода различаются от оператора к оператору и меняются со временем**.
>
> Параллельно, в тот же период, задокументированы и **другие** механизмы блокировки — заморозка соединения после ~16 КБ / ~25 пакетов (метод «tcp 16-20»), CIDR-whitelist по подсетям назначения, SNI-whitelist (см. [net4people/bbs #490](https://github.com/net4people/bbs/issues/490)). Поэтому всё ниже — **снимок одной наблюдаемой волны на июнь 2026**, а не универсальный закон. Конкретные числа читайте как «по наблюдениям автора», а не как точные константы системы.
---
## TL;DR — если совсем коротко
Летом 2026 года перестала надёжно работать связка [[Zapret/about|xray]] + **VLESS + REALITY**.
Важно: REALITY **не взломали**. Его шифрование по-прежнему неотличимо от настоящего TLS. Сломалось другое: DPI перестал заглядывать *внутрь* пакета (это бесполезно) и начал смотреть на **поведение** соединения снаружи.
Блокировка включается, только когда **совпали сразу три признака** (это важно — именно «И», а не «ИЛИ»). Нарушьте любой один — и правило на вас не сработает.
---
## Объясняю на пальцах: что вообще произошло
Представьте таможню на границе (это наш DPI).
Раньше таможенник открывал каждый чемодан и смотрел, что внутри. REALITY — это чемодан с идеальной маскировкой: снаружи и внутри он выглядит как чемодан обычного туриста, едущего на `www.microsoft.com`. Подделку не видно, потому что для этого нужен секретный ключ, которого у таможни нет. Поэтому годами всё работало: «внутри чисто — проходи».
Теперь таможенник **перестал открывать чемоданы** (всё равно бесполезно) и начал смотреть на **поведение пассажира**:
- **Откуда приехал?** — если из района, где живут одни «контрабандисты» (популярные хостинги), это подозрительно.
- **Как выглядит?** — если одет в форму, в которой обычно ходят нарушители (фингерпринт Chrome), это подозрительно.
- **Как себя ведёт?** — если бегает туда-сюда через границу 10 раз в минуту (лавина соединений), это подозрительно.
И только когда **все три** совпали — пассажира «тормозят». Криптография тут уже ни при чём: ловят не *содержимое*, а *манеру*.
---
## Алгоритм блокировки: три сигнала и один триггер
Цензор оценивает **`ClientHello`** — самый первый пакет TLS-рукопожатия — у **каждого** TLS-соединения. Среди прочего он смотрит на такие атрибуты:
> [!quote] Что оценивает цензор (из первоисточника)
> - **IP-адрес сервера** (точнее — его *подсеть*);
> - **SNI** (Server Name Indication — то самое замаскированное имя);
> - **фингерпринт «браузера»** (точнее — того, под кого мимикрируют; у REALITY это делает uTLS).
К этим статическим атрибутам добавляется ещё и **поведение** — частота и параллелизм соединений. «Заморозка» включается, **только когда совпали сразу несколько признаков** (логическое «И»): подозрительная подсеть + подозрительный фингерпринт + аномальная частота к одному SNI. Разберём их по очереди.
> [!note] Почему сигналов «три», хотя атрибутов перечислено больше
> SNI в списке выше — **не отдельный сигнал**, а *ключ агрегации* для поведенческого Сигнала 3: частота соединений считается раздельно по каждому SNI. Поэтому ключевых триггеров остаётся три — **подсеть**, **фингерпринт** и **частота к одному SNI**.
### Сигнал 1. Подсеть сервера — «откуда» вы подключаетесь
DPI смотрит на IP-адрес вашего сервера и проверяет, не входит ли он в список «подозрительных» подсетей. В этот список массово попали диапазоны популярных хостингов, в том числе **российских**:
- **Selectel**
- **Яндекс.Облако**
Логика простая: именно там дешевле и проще всего поднять свой узел, поэтому там их больше всего.
Важный нюанс: схема завязана **не на конкретные IP** отдельных узлов, а на **целые подсети и даже автономные системы (AS)**.
> [!quote] Из первоисточника
> Схема не завязана напрямую на конкретные IP промежуточных узлов (напр., ваших серверов). Это, в общем-то, неплохо — значит, узлы не улетели в некий чёрный список (особенно в свете того, что некоторые приложения «стучат»). Плохая новость в том, что затронуты целые подсети и даже AS, в т.ч. популярных российских дата-центров (Selectel, Яндекс.Облако и др.). Это заметно отличается от прежних методов, которые задевали только зарубежных провайдеров (tcp 16-20, aka l4-25, и пр.).
Что это значит на практике: ваш конкретный IP в чёрный список **не попадает** (его не «сожгли») — под подозрением сразу весь диапазон хостинга. Хорошая сторона — сменить IP в пределах того же провайдера не поможет, но и «гореть» вашему адресу не за что. Плохая — отсюда же берётся сопутствующий ущерб для легальных соседей по подсети (см. раздел про обычный Chrome ниже).
> [!tip] Что это значит для вас
> Сервер в «народном» дата-центре — первый красный флаг. Чем менее предсказуемы провайдер и подсеть, тем лучше. Практику выбора см. в [[VPS/VPS|VPS]].
### Сигнал 2. TLS-фингерпринт клиента — «чем» вы подключаетесь
Когда ваша программа начинает TLS-соединение, она первым делом отправляет пакет **`ClientHello`**. В нём — список поддерживаемых шифров, расширений, их порядок и прочие технические мелочи. Этот набор у каждой реализации TLS свой, как почерк. По нему можно понять, **какая именно программа** сгенерировала пакет (подробнее — в разделе про uTLS ниже).
#### Как выглядит этот «почерк» на самом деле
Возьмём эталонный `ClientHello` из [RFC 8448](https://www.rfc-editor.org/rfc/rfc8448) — это пример прямо из стандарта, поэтому каждый байт можно сверить.
**1. Текстовая форма** — то, как пакет разбирает Wireshark/`tshark` (дерево полей):
```text
TLSv1.3 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22) # 0x16
Version: TLS 1.0 (0x0301) # версия RECORD-слоя (легаси, для совместимости)
Length: 196
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 192
Version: TLS 1.2 (0x0303) # legacy_version — НЕ настоящая версия (см. ниже)
Random: cb34ecb1 e78163ba 1c38c6da … # 32 байта
Session ID Length: 0
Cipher Suites Length: 6
Cipher Suites (3):
TLS_AES_128_GCM_SHA256 (0x1301)
TLS_CHACHA20_POLY1305_SHA256 (0x1303)
TLS_AES_256_GCM_SHA384 (0x1302)
Compression Methods: null (0)
Extensions Length: 145
Extension: server_name → "server" # SNI
Extension: renegotiation_info
Extension: supported_groups → x25519, secp256r1, secp384r1, …
Extension: session_ticket
Extension: key_share → x25519 (32-байтный публичный ключ)
Extension: supported_versions → TLS 1.3 (0x0304) # ← НАСТОЯЩАЯ версия
Extension: signature_algorithms → ecdsa_secp256r1_sha256, …
Extension: psk_key_exchange_modes
Extension: record_size_limit
```
**2. Байтовая форма** — тот же пакет «на проводе» (hex с аннотациями):
```text
16 03 01 00 c4 TLS-запись: Handshake, ver 0x0301, длина 196
01 00 00 c0 ClientHello, длина 192
03 03 legacy_version = 0x0303 (TLS 1.2)
cb 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 ┐
6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 │ Random (32 байта)
02 4d ec e7 ┘
00 Session ID length = 0
00 06 13 01 13 03 13 02 Cipher Suites (6 б): 1301 1303 1302
01 00 Compression: 1 метод = null
00 91 Extensions, длина = 145 байт
00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 server_name → "server"
ff 01 00 01 00 renegotiation_info
00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 … supported_groups
00 23 00 00 session_ticket
00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 … key_share (x25519)
00 2b 00 03 02 03 04 supported_versions → 0x0304 (TLS 1.3)
00 0d 00 20 00 1e 04 03 05 03 … signature_algorithms
00 2d 00 02 01 01 psk_key_exchange_modes
00 1c 00 02 40 01 record_size_limit
```
Фингерпринт — это **набор и порядок** вот этих cipher suites и extensions. Заметьте: `73 65 72 76 65 72` в hex — это ASCII-строка `server` (тот самый SNI, видный **открытым текстом**). Реальный Chrome выглядел бы иначе: GREASE-значения, расширения `ALPN` и `ECH`, post-quantum `key_share` (`X25519MLKEM768`), другой порядок полей. Именно этот «слепок» и сворачивают в JA3/JA4 (см. раздел про uTLS), по нему DPI и опознаёт, кто перед ним.
> [!note] Мелкий, но важный нюанс про версию
> Обратите внимание: в поле версии стоит `0x0303` (TLS 1.2), хотя это TLS 1.3. Так сделано **нарочно** — ради совместимости со старыми серверами. Настоящую версию несёт расширение `supported_versions` (`0x0304`). Поэтому JA4 берёт версию TLS именно оттуда, а не из легаси-поля.
И вот ловушка: xray умеет притворяться чужим почерком, и по умолчанию **почти все ставили Chrome** — он самый популярный, прятаться логичнее в толпе. В итоге сочетание «почерк Chrome + признаки прокси» само стало маркером.
| 🚩 Подозрительные фингерпринты | ✅ «Лояльные» фингерпринты |
| --- | --- |
| Chrome | Firefox |
| Safari | Edge |
| iOS | Android OkHttp |
| | 360 Browser |
| | QQ Browser |
> [!warning] Парадокс
> Самый популярный браузер планеты оказался самым «палевным» — именно потому, что под него массово маскируются. Безопаснее то, под что маскируются реже.
### Сигнал 3. Частота и параллелизм — «как» вы подключаетесь
Третий признак — поведенческий, и именно он чаще всего вас и выдаёт. Точная формулировка триггера из первоисточника:
> **более 3 параллельных попыток установить TLS-соединение, с задержкой между ними менее ~350-400 мс, за последние 60 секунд** — к одному и тому же SNI.
Разберём по словам:
- **более 3 соединений** — то есть 4 и больше;
- **задержка между ними < ~350-400 мс** — то есть они идут залпом, почти одновременно;
- **за последние 60 секунд** — окно, в котором DPI это считает;
- **к одному SNI** — все на одно и то же замаскированное имя (например, `www.microsoft.com`).
Откуда берётся такой залп? Это типичный почерк прокси. Вы открываете браузер, в нём десяток вкладок — и каждая инициирует своё соединение. Все они мультиплексируются на **один** замаскированный SNI. Живой человек, реально открывший `www.microsoft.com`, так себя не ведёт — он не создаёт 10 одновременных коннектов на этот домен.
> [!tip] Что это значит для вас
> Лавина одновременных соединений на один SNI — поведенческая аномалия. Лечится мультиплексированием (`mux`) и распределением по разным SNI (см. раздел про mux ниже).
### Что происходит при срабатывании
Когда совпали **все три** условия, трафик к узлу **«замораживается» на 120 секунд**. Важная деталь: соединения не рвутся демонстративно (это было бы слишком явной цензурой) — они тихо деградируют. Растут таймауты, падает скорость, всё «как будто само тормозит». Внешне похоже на плохой интернет, а не на блокировку.
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
TLS- │ подсеть в │ И │ фингерпринт │ И │ >3 conn/SNI │ ➜ ЗАМОРОЗКА
соединение │ списке? │ │ подозрит.? │ │ <~350-400мс/60с│ 120 сек
───────► │ (Selectel, │ │ (Chrome, │ │ │
│ Я.Облако…) │ │ Safari,iOS) │ │ │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
разорвите ЛЮБОЕ одно звено ➜ правило на вас НЕ срабатывает
```
---
## Почему обычный Chrome работает, хотя «лояльным» его не назвали
Здесь возникает резонный вопрос: если фингерпринт Chrome — «подозрительный», почему миллионы людей спокойно сидят в обычном Chrome и ничего не тормозит? А заодно — почему при этом у части людей вдруг **ломаются некоторые российские сайты**?
Ответ — снова в логике **«И трёх условий»**. Вспомните: чтобы поймать заморозку, нужны **все три** сигнала одновременно. У обычного веб-сёрфинга почти всегда не выполняется **первый** — про подсеть.
**Почему Chrome обычно работает.** Когда вы просто листаете интернет, вы ходите на реальные сервисы — Google, YouTube, банки, маркетплейсы. Они живут на собственной инфраструктуре и больших CDN, которые в список «подозрительных подсетей» не входят (а часто и вовсе в [[Белые списки|белых списках]]). Получается:
- 🚩 Фингерпринт Chrome — да, *подозрительный* (Сигнал 2 ✅);
- лавина параллельных соединений — да, браузер их открывает (Сигнал 3 может сработать);
- **но подсеть назначения — чистая** (Сигнал 1 ❌).
Одно звено цепи разорвано → правило **не срабатывает**. Именно поэтому Chrome для обычного веба «прозрачен»: его спасает не фингерпринт, а **адрес назначения**. А вот ваш личный VLESS-сервер на Selectel/Яндекс.Облаке этот первый сигнал как раз включает — отсюда и разница.
**Почему всё-таки ломаются некоторые ру-сайты.** Это оборотная сторона грубой эвристики «по подсети». В списки подозрительных попали диапазоны **российских** хостингов (Selectel, Яндекс.Облако), а на этих же хостингах живёт масса **легальных** сайтов и сервисов — тех, кто просто арендует там сервер. DPI не умеет отличить «прокси на Selectel» от «интернет-магазина на Selectel» — для него это одна подсеть.
И если вы в обычном Chrome заходите на такой легальный сайт, расположенный на «помеченном» хостинге, может совпасть всё три:
- сайт на подозрительной подсети (Сигнал 1 ✅);
- вы в Chrome (Сигнал 2 ✅);
- браузер открыл несколько быстрых параллельных соединений к его домену (Сигнал 3 ✅).
→ Получаем **ложное срабатывание**: легальный сайт начинает тормозить, хотя никто ничего не обходил. Это и есть «сопутствующий ущерб» (collateral damage) схемы: блокировка по подсети неизбежно задевает добросовестных соседей по хостингу.
> [!summary] Коротко
> Обычный Chrome спасает **чистая подсеть назначения**, а не сам браузер. Где подсеть «грязная» (ваш VPS или легальный сайт на том же хостинге) — там при Chrome и залпе соединений ловят всех подряд, включая невиновных.
---
## Интересный нюанс: ловушка для тех, кто «дёргается»
Самая хитрая часть схемы — реакция на попытку быстро поменять настройки.
Допустим, вас «заморозили». Вы решаете: «наверное, дело в фингерпринте» — и тут же переключаете Chrome на Firefox, переподключаясь. Логично же? Но для DPI это **отдельный сигнал**: только что соединение получило деградацию — и человек моментально сменил TLS-почерк. Так ведёт себя именно тот, кто *осознанно обходит* ограничение.
В ответ прилетает **расширенная блокировка на 600 секунд** — и уже **на все** TLS-соединения к этому узлу. Важно: на эти 10 минут замораживается **любой** TLS, **независимо и от фингерпринта, и от SNI** — сменить почерк или замаскированное имя уже не поможет. Сам TCP-коннект при этом проходит (рукопожатие на уровне TCP устанавливается), душится именно TLS поверх него.
> [!danger] Мораль
> Система ловит не только сам обход, но и **паттерн адаптации**. Нервно крутить настройки под нагрузкой — делать себе хуже. Правильная стратегия: не реагировать рефлекторно, а **изначально** не попадать в цепочку.
---
## 🔬 Технические детали: uTLS — как работает TLS-почерк
Раздел для тех, кто хочет понять механику, а не просто поставить галочку.
### Что такое JA3/JA4 и почему по TLS видно программу
`ClientHello` — это первый, ещё **не зашифрованный** пакет TLS-рукопожатия. В нём открытым текстом передаются:
- версия TLS;
- список **cipher suites** (наборов шифров) — и **в каком порядке**;
- список **extensions** (расширений: SNI, ALPN, supported_groups, signature_algorithms, key_share и т.д.) — и **в каком порядке**;
- эллиптические кривые, форматы точек, GREASE-значения и пр.
Конкретная программа (Chrome, Firefox, Go-приложение, curl) собирает этот набор по-своему: свой порядок шифров, свой набор расширений. Если взять все эти поля и прогнать через хеш, получится **отпечаток**. По нему DPI с высокой точностью говорит: «это Chrome 124» или «это Go-клиент».
Два основных формата отпечатков:
- **JA3** (старый) — это MD5-хеш от полей `ClientHello`, **взятых в том порядке, в каком они идут в пакете**. Минус: Chrome специально **тасует порядок расширений** от соединения к соединению, поэтому JA3-хеш у него «плавает» и легко обходится.
- **JA4** (новый, более устойчивый) — как раз поэтому он **сортирует** шифры и расширения перед хешированием и **игнорирует GREASE**. Это не один хеш, а строка из трёх частей через `_` (`a_b_c`):
- **a** (читаемая): протокол (`t` — TLS-over-TCP, `q` — QUIC, `d` — DTLS), версия TLS, есть ли SNI (`d` = domain / `i` = IP), число шифров, число расширений и ALPN (первый и последний символ значения первого ALPN, напр. `h2`);
- **b**: усечённый SHA256 от **отсортированных** cipher suites;
- **c**: усечённый SHA256 от **отсортированных** extensions (без SNI и ALPN), за которыми идут **signature algorithms — уже без сортировки**.
За счёт сортировки шифров и расширений JA4 не «обманывается» их перетасовкой; при этом signature algorithms намеренно оставлены несортированными как дополнительный различающий признак.
Вот в чём беда TLS-обхода на Go: стандартная библиотека `crypto/tls` имеет **очень узнаваемый фингерпринт** — это прямо признают сами разработчики uTLS («Golang's ClientHello has a very unique fingerprint»). В реальном вебе такой почерк почти не встречается → мгновенный маркер прокси. Поэтому **обычный** VLESS/Trojan поверх TLS, если не указать `fingerprint`, спалится сразу.
#### Контраст «почерков»: голый Go `crypto/tls` vs Chrome
Чтобы было видно, *насколько* они различаются — ключевые отличия в `ClientHello` (структурно; точные байты зависят от версии):
| Признак | 🦫 Голый Go `crypto/tls` | 🌐 Chrome 131+ |
| --- | --- | --- |
| **GREASE** | нет нигде | в 5 местах: первый шифр, первое и последнее расширение, `supported_versions`, `supported_groups`, `key_share` |
| **Порядок расширений** | жёстко фиксирован | тасуется на каждом соединении (с Chrome 110, янв. 2023) |
| **`application_settings`** (ALPS) | нет | есть |
| **`compress_certificate`** (brotli) | нет | есть |
| **ECH** (`encrypted_client_hello`) | нет (по умолчанию) | есть (GREASE-ECH или настоящий) |
| **Число расширений** | ~10–12 | ~17–18 |
| **Cipher list** | 13 шифров, сразу с TLS 1.3 AEAD | начинается с GREASE, характерная браузерная раскладка |
Любого из верхних трёх отличий (нет GREASE, фиксированный порядок, нет ALPS/brotli/ECH) уже достаточно, чтобы пассивно отличить Go-клиент от Chrome. Поэтому «голый» Go без uTLS — мгновенный маркер. (Нюанс актуальности: post-quantum `key_share` `X25519MLKEM768` Chrome шлёт с v131, а Go — только с 1.24; на старых версиях Go отсутствие PQ было ещё одним отличием.)
> [!important] Важный нюанс про REALITY
> В REALITY uTLS **встроен и обязателен**: поле `fingerprint` помечено как Required, а режим отключения uTLS (`unsafe`) официально не поддерживается — REALITY использует эту библиотеку для манипуляции низкоуровневыми параметрами TLS. Поэтому «голый» `crypto/tls`-почерк от REALITY **никогда не уходит** — он всегда отправляет валидный фингерпринт браузера. Проблема REALITY в июне 2026 — не в Go-почерке, а в том, что почерк хоть и корректный, но **слишком массовый** (`chrome`). Об этом — ниже.
### Что делает uTLS
[uTLS](https://github.com/refraction-networking/utls) (`refraction-networking/utls`) — это форк стандартного `crypto/tls`, который позволяет **подменить** содержимое `ClientHello`: порядок шифров, набор расширений, GREASE — по готовому шаблону настоящего браузера. То есть xray генерирует `ClientHello`, **неотличимый** от Chrome или Firefox, и JA3/JA4-хеш совпадает с настоящим браузером. (Технически uTLS работает на уровне готовых пресетов-спецификаций; полный побайтовый контроль возможен, но в обычном режиме хватает пресета, чтобы хеши совпали.)
В конфиге xray это поле `fingerprint` внутри `realitySettings`. Кстати, поле с публичным ключом сервера в актуальных версиях xray-core называется `password` (раньше — `publicKey`; старое имя пока работает для совместимости). uTLS поддерживает пресеты вроде `HelloChrome_Auto`, `HelloFirefox_Auto`, `HelloEdge_Auto`, `HelloSafari_Auto`, `HelloRandomized` и др. В xray они задаются короткими именами: `chrome`, `firefox`, `edge`, `safari`, `ios`, `android`, `360`, `qq`, `random`, `randomized`. То есть рекомендованные в таблице выше «лояльные» 360 Browser и QQ Browser пишутся как `"fingerprint": "360"` и `"fingerprint": "qq"`.
```json
"realitySettings": {
"fingerprint": "firefox",
"serverName": "www.microsoft.com",
"password": "<публичный ключ сервера>",
"shortId": "<short id>"
}
```
### Почему это перестало спасать само по себе
uTLS делает почерк **корректным**, но не делает его **редким**. Если все массово ставят `chrome`, то «JA3 Chrome + поведение прокси» становится статистически заметным. DPI не говорит «фингерпринт поддельный» (он валидный!) — он говорит «слишком много именно такого фингерпринта летит на этот странный сервер залпами». Поэтому в июне 2026 совет сместился: брать **не самый массовый** профиль — `firefox`/`edge` и пр.
> [!note] Про `random` и `randomized` — это РАЗНЫЕ режимы
> Их легко перепутать, но ведут себя они по-разному:
> - **`random`** — xray случайно берёт один из готовых **реальных** пресетов браузера (Chrome 131, Firefox 148 и т.п.). Почерк всегда валидный — это безопасный вариант.
> - **`randomized`** (и `randomizednoalpn`) — **синтетическая** генерация: расширения и шифры добавляются/тасуются случайно. Может получиться комбинация, **которой не существует ни у одного реального браузера** — а это само по себе аномалия.
>
> Вывод: бездумно ставить `randomized` не стоит. Лучше явный «лояльный» пресет (`firefox`/`edge`) или, на худой конец, `random` — но не синтетическую рулетку.
### uTLS не маскирует тайминги
Ключевой момент: uTLS правит только **содержимое** `ClientHello`. Он **никак не влияет** на *Сигнал 3* — на то, сколько соединений и с какой частотой вы открываете. Почерк может быть идеальным, но если вы шлёте на один SNI залп коннектов с интервалом между ними менее ~350-400 мс — вас всё равно поймают по поведению. Вот почему одного uTLS мало, и нужен mux.
---
## 🔬 Технические детали: mux — как сгладить лавину соединений
`mux` (мультиплексирование) — это прямое лекарство от *Сигнала 3*.
### Проблема, которую он решает
Без mux каждая вкладка / каждое приложение = **отдельное физическое TCP+TLS-соединение** до сервера. Открыли почту, мессенджер, пару сайтов — и вот уже залп одновременных коннектов на один замаскированный SNI. Ровно тот паттерн, который ловит DPI.
### Как работает mux в xray
Мультиплексирование загоняет **много логических потоков внутрь одного физического соединения**. Снаружи DPI видит **одно** TLS-соединение, по которому идёт постоянный поток данных, — как у обычного пользователя, открывшего тяжёлый сайт. Залпа новых рукопожатий нет → *Сигнал 3* не срабатывает.
```json
"mux": {
"enabled": true,
"concurrency": 8,
"xudpConcurrency": 16,
"xudpProxyUDP443": "reject"
}
```
Разберём параметры:
- **`concurrency`** — сколько логических потоков пускать в одно физическое соединение (для TCP). Допустимый диапазон — `1…128`, по умолчанию `8`. Когда лимит исчерпан, открывается новое физическое соединение. Любое отрицательное значение (`-1`) отключает mux для TCP.
- **`xudpConcurrency`** — то же самое, но для UDP-трафика поверх механизма XUDP (нужно для игр, звонков, QUIC). Диапазон у него **другой** — `1…1024` (а не 1…128, как у TCP); при `0` или отсутствии XUDP-трафик идёт по тому же пути, что и обычный TCP.
- **`xudpProxyUDP443`** — что делать с UDP на 443-м порту (это QUIC/HTTP3). Часто ставят `reject`, чтобы QUIC падал обратно на TCP — так трафик идёт по нашему замаскированному каналу, а не утекает мимо.
> [!warning] У mux есть и обратная сторона
> - **Head-of-line blocking**: все логические потоки делят одно TCP-соединение. Потеря одного пакета тормозит сразу все потоки внутри. На нестабильном канале это ощутимо бьёт по скорости.
> - **Слишком большой `concurrency`** склеивает весь трафик в один долгоживущий коннект — тоже своего рода аномалия (реальные браузеры всё же открывают несколько соединений). Истина посередине: умеренные значения вроде `4–8`.
> - Для **скоростных загрузок** mux иногда наоборот мешает — один коннект упирается в лимиты. Поэтому его часто включают выборочно.
> [!danger] mux и XTLS Vision: TCP-mux не применяется
> Это важно именно для нашей связки. Сейчас стандартный поток для VLESS+REALITY — **XTLS Vision** (`"flow": "xtls-rprx-vision"`). Vision работает на «сыром» (raw) TCP-соединении со `splice`-копированием в ядре Linux, поэтому **TCP-mux при Vision штатно не применяется** — это поведение самого Xray-core, а не сбой (в формулировке ядра: «MUX is not compatible with XTLS raw connections»).
>
> На практике клиенты при Vision генерируют такой блок: `"enabled": true`, `"concurrency": -1` (TCP-mux выключен), но `"xudpConcurrency"` оставлен — чтобы мультиплексировать хотя бы UDP/XUDP. Учтите: при `"enabled": false` XUDP-mux тоже не активируется.
>
> Вывод: не копируйте `"concurrency": 8` бездумно поверх Vision-конфига. Если у вас Vision, бороться с *Сигналом 3* придётся другими средствами — в первую очередь разнесением по разным SNI и сдержанным паттерном соединений.
### Дополнительно: разносите SNI
Триггер *Сигнала 3* считается **на один SNI**. Если раскидать трафик по нескольким разным SNI (см. большой список в [[Zapret/vless-sni|vless-sni]]), нагрузка не концентрируется на одном имени, и порог «>3 соединений с интервалом <~350-400 мс за 60 с» на каждое отдельное имя достигается труднее. mux + несколько SNI вместе очень эффективно сглаживают поведенческий след.
---
## ✅ Что делать: рвём цепочку (по шагам)
Поскольку триггер — это «И» трёх условий, для безопасности достаточно стабильно нарушать **хотя бы одно** звено. На практике стоит закрыть несколько — для запаса прочности.
### Шаг 1. Сменить фингерпринт на «лояльный»
Самое дешёвое действие. В `realitySettings` поменять `fingerprint` с `chrome` на `firefox` или `edge`:
```json
"realitySettings": {
"fingerprint": "firefox"
}
```
### Шаг 2. Включить `mux` и снизить параллелизм
Сглаживаем залп соединений:
```json
"mux": {
"enabled": true,
"concurrency": 8
}
```
И дополнительно — **разнести трафик по нескольким SNI** ([[Zapret/vless-sni|список SNI]]).
> [!warning] Если у вас XTLS Vision
> При стандартном для REALITY flow `xtls-rprx-vision` TCP-mux **не применяется** (см. раздел про mux и Vision выше), так что `concurrency: 8` тут не сработает. Тогда против Сигнала 3 работают только **разные SNI + сдержанный паттерн**, либо переход на **XHTTP+XMUX** (см. FAQ «Можно ли вылечить обе болезни сразу?»).
### Шаг 3. Увести сервер в «невидимую» подсеть
Уходим из массовых диапазонов народных хостингов (Selectel, Яндекс.Облако и т.п.). Менее предсказуемый провайдер/подсеть → выпадаем из списка «подозрительных». Подбор — в [[VPS/VPS|VPS]].
### Шаг 4. Не дёргаться под нагрузкой
Поймали деградацию — **не меняйте фингерпринт на лету**. Лучше переждать окно (120 с) или сменить узел целиком, чем спровоцировать расширенную блокировку на 600 секунд.
> [!note] «Смени фингерпринт» — не единственный и не универсальный рецепт
> Совет про `firefox`/`edge` — снимок одной волны, и он не гарантирован. Что важно держать в голове:
> - **Свежесть пресета uTLS важнее выбора браузера.** К концу 2025 — началу 2026 более половины «человеческих» браузерных соединений (по данным Cloudflare Radar) несут **post-quantum** key_share (`X25519MLKEM768`) — он стал дефолтом в Chrome 131 (ноя 2024) и Firefox 132 (окт 2024). Пресет без него, выдающий себя за свежий браузер, уже сам по себе аномален — следите, чтобы uTLS/xray были свежими.
> - **Не путайте с обходами ДРУГИХ блокировок.** Советы «пустой SNI» и «нестандартный порт (47000+)» относятся к более ранней *сигнатурной* блокировке на порту 443 (нач. 2025) и к ограничению по числу TLS-соединений — «сибирскую» схему (подсеть + фингерпринт + частота) они логически не нарушают. `XHTTP` помогает лишь как способ снизить число соединений (это уже делает `mux`), а Shadowsocks-2022 — не обход, а смена протокола (у него нет ни SNI, ни uTLS-почерка браузера). Столкнулись именно с этой схемой — работает только нарушение её трёх условий.
### Проверить себя: dpi-checkers
Автор первоисточника ведёт инструмент **[dpi-checkers](https://github.com/hyperion-cs/dpi-checkers)** (на момент разбора — версия **v0.7.0**). Он прогоняет вашу инфраструктуру по описанным критериям: попадает ли подсеть в подозрительные, как выглядит фингерпринт, ловится ли поведенческий паттерн. Проверки задаются в YAML, например фильтр по организации хостинга:
```yaml
checkers:
webhost:
infra:
- name: Selectel
filter: org("selectel")
```
---
## ❓ Слабые места и частые вопросы
Несколько вопросов, которые закономерно возникают после прочтения.
### Фиксированный список фингерпринтов uTLS — это слабое место?
**Да.** `firefox`, который у вас «снова заработал», — это **снимок конкретной версии** (например `HelloFirefox_120`). И его тоже могут начать ловить. Почему список — структурная слабость:
- **Устаревание.** Пресет заморожен во времени, а живой браузер обновляется каждые недели. `HelloChrome_120` со временем становится *редкой старой* версией на фоне Chrome 13x — особенно теперь, когда у свежих браузеров появился post-quantum `key_share`, которого у старого пресета нет.
- **Перечислимость.** Список открытый и небольшой (~50 пресетов в `u_common.go`). Цензор может просто выписать все JA3/JA4 пресетов uTLS и сверять с распределением реальных браузеров.
- **Несовершенство «попугая».** Даже совпав по версии, uTLS иногда отличается в деталях — вплоть до уязвимостей с CVE: например рассогласование GREASE/ECH-шифров в Chrome-пресете давало комбинацию, невозможную у настоящего Chrome (CVE-2026-27017, закрыто в uTLS 1.8.1).
**Что вас пока спасает:** заблокировать *популярный валидный* фингерпринт целиком цензору дорого — сломаются реальные пользователи Firefox/Chrome. Слабость возникает, когда отпечаток **одновременно** коррелирует с прокси **и** достаточно редкий, чтобы блок был «дешёвым».
### Можно отсканировать свой реальный браузер и залить отпечаток в uTLS-клиент?
И да, и нет — важно разделить **библиотеку** и **конечный клиент**:
- **В библиотеке uTLS — можно.** `Fingerprinter.FingerprintClientHello(rawBytes)` берёт **сырые байты** реального `ClientHello` (захваченные Wireshark'ом — *не* JA3-хеш, он «лоссовый»), строит `ClientHelloSpec`, применяемый через `HelloCustom`.
- **В xray-core и sing-box — НЕЛЬЗЯ.** Оба ограничены меню пресетов; подсунуть свой захваченный `ClientHello` в конфиг нельзя. Это просили напрямую — [xray-core issue #1782](https://github.com/XTLS/Xray-core/issues/1782) **закрыт как «not planned»**.
- **Сканеры своего браузера живы:** [browserleaks.com/tls](https://browserleaks.com/tls) (самый подробный), [tls.peet.ws](https://tls.peet.ws/) (JSON-API). Но они выдают хеши/сводку, **а не raw-байты**, нужные `Fingerprinter`.
Итого: **готового пайплайна для обычного пользователя нет** — это ручная работа разработчика (pcap → `FingerprintClientHello` → свой Go-код). В стандартном клиенте доступны только пресеты + `random`/`randomized`.
> [!warning] Парадокс уникальности
> Если зальёте *свой уникальный* отпечаток — он станет маркером именно **вас**. Сила браузерного фингерпринта в том, что он *массовый*; уникальный — наоборот, деанонимизирует.
### Альтернатива uTLS-пресетам: реальный стек Chromium (naive/cronet)
Раз uTLS — это **попугай** (заморожённый пресет, который перечислим и со временем протухает, а ручной скан своего браузера в стандартный клиент не залить), напрашивается другой путь: не *имитировать* браузер, а **взять настоящий сетевой стек браузера**. Тогда JA3/JA4 — не копия, а подлинник, и обновляется он вместе с браузером.
Это удобно представить как **лестницу аутентичности почерка** (от худшего к лучшему):
| Подход | Почерк | Свежесть | Мультиплексирование |
| --- | --- | --- | --- |
| **uTLS-пресет** (`chrome`/`firefox`) | копия, перечислимая | протухает (нужно ждать новый пресет) | нет (нужен отдельный mux) |
| **NaiveProxy** (Chromium `//net`/cronet) | подлинный стек Chromium | **отстаёт**: автор вручную линкует свежий хромиум; на момент обсуждения был Chrome 149, а Naive — на сборке ~148.x | да, естественный HTTP/2 |
| **XHTTP + Browser Dialer** (Xray) | подлинный — TLS делает **реально установленный в системе браузер** | **моментальная**: берётся любой обновлённый браузер из системы | да, в паре с **XMUX** (`maxConnections`) |
> [!tip] Закрывает сразу две оси — но **двумя рычагами**, не «одной галочкой»
> Подлинный стек браузера даёт **массовый настоящий фингерпринт** — это **Сигнал 2** (дорого банить, не «протухает», как пресет). А вот поведенческую ось (**Сигнал 3**) закрывает **не сам почерк, а транспорт**: HTTP/2 + `XMUX`. У **NaiveProxy** мультиплексирование нативное (HTTP/2 в cronet); у **Browser Dialer** его нужно **включить отдельно** — `XMUX`/`maxConnections` в XHTTP, само по себе оно не появляется. То есть это **два рычага в связке** (подлинный почерк + mux), а не один инструмент «из коробки» — зато оба живут в одном клиенте, тогда как «uTLS + отдельный mux» собираются из более хрупких частей. Про поведенческую ось — см. раздел про **mux** в этой заметке; `XMUX` в xhttp придумали ещё **до** «сибирской» схемы — изначально ради пулинга соединений под CDN, но как **побочный эффект** это и сглаживает лавину (Сигнал 3).
Нюансы (по сообщениям сообщества, не как гарантия):
- **NaiveProxy** всё ещё **не свободен от устаревания**: cronet-стек влинкован под конкретную сборку Chromium, и пока автор не пересоберёт под новый — поведение свежего Chrome может отличаться в деталях. То есть Naive **уменьшает** разрыв с реальным браузером, но не обнуляет его.
- **Browser Dialer** использует **уже установленный** браузер, поэтому переход на новую версию мгновенный и попугайства нет вообще — но цена в том, что нужен живой браузер/вкладка-«диалер» рядом с клиентом (UX-издержка); по сообщениям, на Android рабочий вариант.
- Это **клиентский** путь: почерк генерится **на устройстве пользователя**, до цензора — ровно то, о чём [[mtproxy/ja4-sni-client-side|«JA4 и SNI меняет только клиент»]]. Серверная сторона тут по-прежнему ничего не решает.
> [!warning] Не «серебряная пуля»
> Подлинный почерк снимает только **Сигнал 2** (фингерпринт) и, через XMUX/HTTP-2, **Сигнал 3** (частоту). **Сигнал 1 (подсеть VPS)** остаётся — его убирает только CDN-фронтинг (см. ниже «Что реально помогает против сигнала подсети»). И сам по себе подлинный стек не прячет **TLS-в-TLS** — это отдельная ось (Vision/padding).
### Имеет ли смысл держать на VPS свою страничку и использовать её как SNI?
**Для REALITY — нет, это ломает саму идею.** REALITY не использует ваш домен — он **заимствует TLS-личность чужого популярного сайта**: вы указываете `target`/`dest` = реальный внешний сайт, REALITY ретранслирует к нему рукопожатие, и наблюдатель видит **настоящий сертификат этого сайта**. Своя страничка как SNI:
- **теряет заёмную репутацию** (а это весь смысл REALITY) — неизвестный `myhomepage.tld` репутации не имеет;
- **не входит в SNI-whitelist** — при белых списках неизвестное имя *подозрительнее* знаменитого;
- **концентрирует частоту** — весь трафик на один свой SNI = ровно та аномалия «много к одному редкому имени», которую ловит Сигнал 3.
Decoy-сайт (своя страница + nginx-fallback) — это про **классическую** маскировку (Trojan / VLESS + реальный сертификат) и помогает он против **активного зондирования**, а не против этой пассивной схемы. Если уж REALITY напрямую на VPS — берите чужой *знаменитый* SNI, не свой.
### Не устарел ли сам REALITY? (сверка пары SNI↔IP)
Отдельный довод, который звучит у практиков: REALITY становится уязвим к **сверке пары SNI↔IP**. Вся идея REALITY — *заимствовать* чужой знаменитый SNI (например `www.microsoft.com`), но соединение при этом идёт на **ваш VPS-IP**, а не на реальные адреса Microsoft. Пассивный цензор, ведущий карту «какой SNI обычно ходит на какие IP/ASN», может ловить **рассогласование**: в `ClientHello` заявлен `www.microsoft.com`, а пакет летит в случайную подсеть хостинга. Активное зондирование тут ни при чём — это **пассивная** проверка, и фирменная защита REALITY «отдать настоящий серт на пробу» от неё **не спасает** (это другая ось).
> [!warning] «REALITY бесполезен» — это гипербола
> Сверка SNI↔IP **дорога цензору**: легитимных рассогласований масса (CDN, ECH, сплит-DNS, корпоративные прокси), поэтому массовая проверка даёт ложные срабатывания на обычном трафике — тот же «сопутствующий ущерб», что и с подсетями. Корректнее формулировать как «вектор реален и REALITY **слабеет**», а не «мёртв». И главное: это **мнение/прогноз практиков**, а **не** подтверждённая часть «сибирской» схемы — в первоисточнике три сигнала (подсеть, фингерпринт, частота), **сверки SNI↔IP там нет**.
Это **родственная** логика той, что осторожно отмечена для MTProto в [[mtproxy/ja4-sni-client-side|проверке SNI на резолвимость]]: в обоих случаях цензор сверяет заявленное имя с реальностью соединения. Но **сами проверки разные**: для MTProto — *резолвится ли SNI вообще* (есть ли A-запись); для REALITY — *совпадает ли SNI с IP назначения*. И там, и там это пока **гипотеза о развитии DPI**, а не зафиксированное правило.
И ещё довод против «фирменности» REALITY: его защита от активного зондирования — это **fallback** (редирект неаутентифицированных проб на `dest`-бэкенд, «spider mode»), и поведенчески она воспроизводится **десятком строк** в caddy/nginx (обычный TLS + fallback на реальный сайт). То есть по-настоящему уникальна у REALITY именно **заёмная личность** чужого *удалённого* сайта — а её как раз и подтачивает сверка SNI↔IP. (Для ясности: **self-steal** — это не защита, а *топология* REALITY, где `dest`/`target` смотрит на **свой локальный** decoy, т.е. ровно тот самый «своя страница + nginx-fallback», только средствами REALITY — противоположность заимствованию чужого сайта.) При этом против самого опасного, **Сигнала 1 (подсеть)**, REALITY не помогал никогда — поэтому общий вывод не меняется: структурный рычаг — **CDN-фронтинг** (см. ниже), а не выбор «магического» транспорта.
### Что реально помогает против сигнала «подозрительная подсеть»?
Все локальные меры (свежий пресет, mux, разные SNI, выбор провайдера) **не убирают Сигнал 1** — подсеть VPS остаётся подозрительной. Единственный рычаг, который **структурно** его убирает, — спрятать назначение за **большим CDN**:
- **VLESS over XHTTP / WebSocket за Cloudflare.** Цензор видит соединение к **IP Cloudflare** (огромный, де-факто whitelisted), а не к вашему VPS — сигнал подсети исчезает, потому что меняется *сам адрес, который видит цензор*.
- **Цена честно:** CDN терминирует ваш TLS и **видит трафик** в открытом виде; **медленнее** (крюк через edge); **ToS-серая зона** для тяжёлого/видео-трафика.
- Важно: сам REALITY за CDN так не ставится — ради CDN меняют транспорт (XHTTP/WS).
#### Что от чего помогает: active probing vs пассивная схема
| Мера | Против active probing | Против пассивной схемы (подсеть + фингерпринт + частота) |
| --- | --- | --- |
| REALITY со **знаменитым чужим** SNI | ✅ | ⚠️ частично — знаменитый SNI размывает частоту, но **подсеть не меняется** |
| REALITY со **своим decoy-SNI** | ✅ | ❌ хуже — редкий SNI концентрирует трафик; подсеть та же; не в whitelist |
| Классика VLESS/Trojan + свой серт + nginx-fallback | ⚠️ частично | ❌ — свой домен + та же подсеть |
| **CDN-фронтинг** (VLESS+XHTTP/WS за Cloudflare) | ✅ | ✅ **единственный рычаг, убирающий сигнал подсети** (цена: CDN видит трафик, медленнее, ToS) |
### Прямое подключение vs каскад — спасает ли это от Сигнала 1?
Короткий ответ: **нет — решает не топология, а флагнутость подсети первого хопа.**
Сигнал 1 смотрит **не на вашу домашнюю подсеть** (residential-IP в РФ), а на **подсеть назначения того соединения, которое видит DPI** — то есть на **первый хоп**, сервер, к которому вы открываете TLS.
- **Прямое `юзер → eu-нода`.** DPI видит соединение к зарубежному серверу. И зарубежные хостинги **тоже** в списке подозрительных — более того, они там были **изначально**: старые методы (`tcp 16-20` / `l4-25`) били **только** по зарубежным провайдерам (Hetzner, DigitalOcean, OVH). Новизна «сибирской» схемы — что к ним **добавили** российские ДЦ, а не заменили. Так что прямое подключение к VPS на Hetzner/DO **спокойно попадает** под Сигнал 1.
- **Каскад `юзер → ru-нода → eu-нода`.** DPI видит вход — **ru-ноду**. Если она на Selectel/Яндекс.Облаке, это **теперь тоже** подозрительная подсеть, то есть каскад через российский ДЦ стало только **хуже**. (Хоп ru→eu — исходящий трафик сервера, ваш DPI его как клиентское рукопожатие не оценивает.)
Сигнал 1 **не срабатывает** не «при прямом» и не «при каскаде», а когда подсеть первого хопа **вообще не в списке**: residential/мобильные IP, большой CDN (поэтому CDN-фронтинг и есть структурный рычаг), редкий «чистый» провайдер. Hetzner напрямую и Selectel через каскад «горят» одинаково; residential или CDN — нет, в любой топологии.
> [!quote] Оговорка
> Точный состав списка подозрительных подсетей публично неизвестен (реверс-инжиниринг одного автора). Вывод «зарубежные тоже в списке» следует из формулировки первоисточника («затронуты подсети/AS, **в т.ч.** российских ДЦ, в отличие от методов, бивших **только** по зарубежным»), а не из опубликованной спецификации.
### Разве XTLS Vision не маскирует большое количество TLS-соединений?
Частая путаница. **Нет — Vision маскирует не *количество* соединений, а *форму* TLS-в-TLS внутри одного соединения.**
- Vision (`xtls-rprx-vision`) решает проблему **«TLS-in-TLS detection»**: когда прокси несёт внутренние TLS-записи внутри внешнего TLS, возникает узнаваемый вложенный паттерн (характерные размеры записей). Vision добавляет padding в раннюю фазу рукопожатия и затем переключается на прямое `splice`-копирование в ядре — внешне трафик перестаёт выглядеть как «TLS в TLS».
- Но Vision работает **по-соединению** и **ничего не объединяет**. Каждое проксируемое соединение — это всё равно свой отдельный внешний TLS+TCP-коннект к серверу. Браузер открыл 10 вкладок → 10 внешних TLS-соединений на один SNI. Это ровно территория **Сигнала 3**.
- Объединяет много логических потоков в одно физическое соединение — это **`mux`**, а не Vision. И именно поэтому они конфликтуют (см. раздел про mux выше): Vision требует «сырого» TCP под splice, а mux оборачивает поток.
Вывод: Vision прячет *вложенность* TLS, но **не уменьшает число соединений**. Против поведенческого Сигнала 3 он не помогает — это задача mux (несовместимого с Vision) либо разнесения по разным SNI и сдержанного паттерна.
### Vision, mux и «TLS-в-TLS» — три вещи, которые путают
Самый запутанный узел. Тут сходятся **три независимых** понятия — разведём их:
| Ось | Что это | Чем управляется |
| --- | --- | --- |
| **Security** (`reality`/`tls`) | чьей TLS-личностью представляется внешний туннель | выбор `security` |
| **TLS-в-TLS** | вложенность: сайт-TLS *внутри* туннеля | возникает сам при HTTPS-сёрфинге → лечит **Vision** |
| **Число внешних соединений** | сколько отдельных TCP+TLS к серверу | `mux` (вкл/выкл) → считает **Сигнал 3** |
Главное: **`reality` vs `tls` НЕ решает «отдельные коннекты или один поток»** — это решает `mux`. И reality, и tls по умолчанию дают одно внешнее соединение на коннект и оба несут TLS-в-TLS.
**Что такое TLS-в-TLS** (с этим борется Vision). Открываете `https://google.com` через туннель — получаются два вложенных слоя TLS:
```text
[ внешний TLS / REALITY-туннель : вы ↔ ваш VPS ]
└── внутри: [ внутренний TLS : браузер ↔ google.com ] ← «TLS в TLS»
```
Даже ОДНО соединение уже имеет эту вложенность: размеры записей внутреннего рукопожатия «просвечивают» сквозь внешний слой. Vision добавляет padding и переключается на splice, чтобы внутренний почерк не просвечивал. Это есть и при `reality`, и при `tls`.
**А «один большой поток с кучей рукопожатий» — это `mux`** (а не `security: tls`!):
```text
[ ОДНО внешнее TLS-соединение ]
├── подпоток 1 → сайт A (внутри свой TLS)
├── подпоток 2 → сайт B
└── подпоток 3 → сайт C
```
Много потоков в одном коннекте → меньше внешних соединений → бьёт по Сигналу 3. Но Vision на mux-поток **не накладывается** — ему нужен «сырой» отдельный TCP под splice, поэтому Vision и mux **взаимоисключающие**.
> [!summary] Развязка
> - **Vision** лечит **TLS-в-TLS** (форму *внутри* одного соединения) — это отдельный детект, **не** из «сибирской» тройки.
> - **mux** лечит **число соединений** (Сигнал 3).
> - Они чинят разные болезни и **взаимоисключающи** — выбираете что-то одно.
> - `security` (reality/tls) — только про то, *чьим* сертификатом представляется туннель; к «коннекты vs поток» отношения не имеет.
### Можно ли вылечить обе болезни сразу?
Раз Vision и mux взаимоисключающие — обречены ли мы выбирать? **Нет.** И тут важное переосмысление: сам конфликт — **не «защита против защиты»**.
Vision склеивает две разные вещи:
- **padding** — добивает короткие пакеты ранней фазы рукопожатия → это и есть **анти-детект** TLS-в-TLS;
- **splice** — после рукопожатия копирует поток прямо в ядре Linux → это **оптимизация производительности**, и только.
С mux несовместим именно **`splice`** (ему нужен «сырой» 1:1 поток), а **не `padding`**. То есть настоящий трейд-офф — **«padding + splice-скорость» против «мультиплексирования»**, а не «один детект против другого». Splice — это то, чем жертвуют, а не защита. **Значит, обе болезни лечатся одновременно — ценой отказа от splice-ускорения.**
**Практическое воплощение — транспорт XHTTP + XMUX** ([«XHTTP: Beyond REALITY», #4113](https://github.com/XTLS/Xray-core/discussions/4113)) — VLESS поверх HTTP/2 или H3:
- **число соединений** лечит **XMUX** (объединяет логические запросы в одно физическое соединение) — ✅;
- **форму TLS-в-TLS** размывает (header-padding `xPaddingBytes` + разнесение upload/download + перемешивание потоков);
- **совместим с REALITY** и **работает за CDN** — а это убирает заодно и Сигнал 1 (подсеть становится CDN'овской).
| | Vision + REALITY (raw TCP) | XHTTP + XMUX (+ REALITY/CDN) |
| --- | --- | --- |
| TLS-в-TLS | ✅ padding + splice (сильнее по форме) | ⚠️ padding + split + mux (статистически, слабее) |
| Число соединений (Сигнал 3) | ❌ (mux нельзя) | ✅ XMUX |
| Подсеть (Сигнал 1) | ❌ | ✅ если за CDN |
| Скорость | ✅ splice | ❌ всё через userspace (медленнее) |
> [!warning] Честная оговорка
> XHTTP прячет TLS-в-TLS **слабее**, чем кажется. По [USENIX Security 2024](https://www.usenix.org/conference/usenixsecurity24/presentation/xue-fingerprinting) («Fingerprinting Obfuscated Proxy Traffic with Encapsulated TLS Handshakes») padding + мультиплексирование «многообещающи, но **принципиально ограниченны**»: они не уменьшают размер всплесков и число round-trip'ов, по которым вложенное рукопожатие и палится. XHTTP делает детект **статистически дороже**, а не невозможным. *Почему* именно — разобрано ниже, в пункте про round-trip'ы.
**Итог:** вылечить обе можно (XHTTP+XMUX), цена — потеря splice-скорости (а не принципиальный запрет) плюс «статистический», а не идеальный анти-TLS-в-TLS. Поэтому XTLS рекомендует держать **оба профиля** на сервере: `Vision+REALITY+TCP` для скорости/прямого подключения и `XHTTP+XMUX` для стелса/CDN (популярная «5-в-1» конфигурация).
### А kTLS — вернёт ли он потерянную скорость?
**kTLS** (Kernel TLS) — функция ядра Linux, переносящая шифрование/расшифровку TLS-записей **в ядро**. За счёт этого forwarding может работать с zero-copy (`sendfile`/`splice`) даже когда прокси сам терминирует TLS. В xray это обсуждается ([discussion #4270](https://github.com/XTLS/Xray-core/discussions/4270), [issue #2565](https://github.com/XTLS/Xray-core/issues/2565)) как способ получить splice-уровень производительности **без** XTLS Vision — в обычном TLS-проксировании.
Но важно, чем kTLS **не** является:
- Это **только производительность**, не анти-детект. Он ничего не прячет — ни форму TLS-в-TLS, ни число соединений.
- Он **не отменяет** дилемму mux↔splice: ядерное копирование всё равно несовместимо с мультиплексором, который оборачивает поток в свои фреймы.
- В xray это пока **экспериментально / на стадии обсуждения**, а не готовая кнопка.
Грубо: kTLS — рычаг «вернуть часть CPU и скорости», **ортогональный** детекту. На вопрос «как спрятаться» он не отвечает; он про «как сделать это дешевле по ресурсам».
### Почему padding и mux в принципе не прячут TLS-в-TLS (детект по round-trip'ам)
Самый глубокий уровень — *почему* padding и мультиплексирование лишь полумеры **против детекта TLS-в-TLS**. (Важно: это отдельный вектор, **не** «сибирский» Сигнал 3 — число параллельных соединений mux по-прежнему уменьшает; речь о том, что *форму* вложенного рукопожатия полностью спрятать не удаётся.) Объясняет это [USENIX Security 2024](https://www.usenix.org/conference/usenixsecurity24/presentation/xue-fingerprinting).
TLS-рукопожатие — это характерная **хореография**: всплеск пакетов от клиента → пауза в один **RTT** (round-trip) → ответный всплеск от сервера → снова пауза → и т.д. Когда такое рукопожатие идёт **внутри** туннеля, эта последовательность «всплеск — пауза RTT — всплеск» **просвечивает наружу** узнаваемым ритмом в начале соединения.
И вот суть, почему обфускация не спасает:
- **padding** меняет *размеры* пакетов — но **не меняет число round-trip'ов** и не убирает структуру всплесков. Ритм остаётся.
- **mux** перемешивает потоки — но при малой нагрузке (один-два активных потока) перемешивать нечего, и начальное рукопожатие сохраняет свой round-trip-узор.
Поэтому детектор смотрит не на байты, а на **последовательность всплесков, их направления и паузы между ними** — и опознаёт «внутри идёт TLS-рукопожатие». Это устойчиво к padding'у и частично к mux'у: в той работе обфусцированные прокси в стандартных конфигурациях ловятся с TPR >70%, а агрессивный padding (как у XTLS-Vision) сбивает это лишь до **~51%** — труднее, но не спасает.
> [!summary] Что нужно для настоящей защиты
> По выводам статьи — не *дополнять* трафик, а **переформировывать** его: уменьшать размер всплесков и число round-trip'ов внутри соединения. Готового решения, полностью закрывающего это, в современных клиентах пока нет. Отсюда и фундаментальность гонки: обфускация поднимает *стоимость* детекта, но не делает его невозможным.
### Можно ли mux без head-of-line blocking?
Раз у mux есть болячка HoL (потеря одного пакета тормозит все потоки) — можно ли мультиплексировать без неё? **Можно, и это уже сделано** — но сначала о корне.
**Откуда берётся HoL.** TCP — это **один упорядоченный надёжный байтовый поток**. Mux поверх него гонит все логические потоки через этот единый поток. Потерялся пакет → TCP держит всё, что пришло **после** него, пока не дождётся повтора — даже данные других потоков, которые уже дошли целыми. Это **неизбежно** для любого mux поверх одного TCP: ни Mux.Cool, ни HTTP/2 не уйдут — они все сидят на одном TCP-потоке (HTTP/2 убрал HoL на уровне приложения, но не транспорта).
**Выход — не использовать единый TCP-поток.** Надо дать каждому логическому потоку **свою независимую упорядоченность**. Это умеет **QUIC (= HTTP/3)**: работает поверх UDP и реализует надёжность/порядок **на каждый поток отдельно**. Потерянный пакет блокирует только свой поток — остальные едут дальше. Ради этого QUIC и придумали.
**В xray это уже есть:** XHTTP поддерживает **H3-режим** (QUIC) → XMUX поверх QUIC = мультиплексирование **без TCP-HoL**.
> [!warning] Подвохи — почему это не дефолт
> - **QUIC = UDP, а UDP легко душат.** Цензор может троттлить/резать UDP или QUIC целиком (в РФ бывает) → откат на TCP, и HoL возвращается. Отсюда же `xudpProxyUDP443: reject` (форсить откат QUIC на TCP).
> - **QUIC не прячет детект.** Round-trip'ы вложенного рукопожатия (см. пункт выше) просвечивают и через него — QUIC лечит *производительность/HoL*, а не *обнаружимость формы*.
> - **Цена и совместимость.** QUIC — userspace, дороже по CPU; поддержка H3 у CDN/промежуточных узлов неровная.
**Итог:** HoL **неустраним** поверх одного TCP — это закон. Обойти = перейти на per-stream-транспорт (**QUIC/H3**, XHTTP умеет), но вы меняете «HoL + надёжный TCP» на «без HoL, но по UDP, который цензор может прибить» — и форму трафика это всё равно не лечит. Бесплатного обеда нет.
---
## 🎯 Главный вывод
Эпоха «поставил REALITY и забыл» закончилась. DPI перешёл от анализа **содержимого** пакета к анализу **поведения** соединения. Отсюда три следствия:
1. Идеальная криптография **больше не гарантирует** невидимость — она необходима, но недостаточна.
2. Решает не один параметр, а **совокупность косвенных признаков** (подсеть + фингерпринт + поведение).
3. Устойчивость даёт не «волшебная галочка», а **грамотный профиль целиком**: правильная подсеть, лояльный фингерпринт (uTLS), сдержанный паттерн соединений (mux + разные SNI).
Гонка щита и меча перешла на уровень статистики и поведения. Выигрывает тот, чей трафик **скучный** — максимально похожий на обычного пользователя и снаружи, и внутри.
---
## 📚 См. также
- 🔗 **Первоисточник:** [habr.com/ru/articles/1044396](https://habr.com/ru/articles/1044396/) — Пётр Осетров (@hyperion_cs)
- 🔗 [uTLS — refraction-networking/utls](https://github.com/refraction-networking/utls)
- 🔗 [dpi-checkers — hyperion-cs/dpi-checkers](https://github.com/hyperion-cs/dpi-checkers)
- 🔗 [xray-core — XTLS/Xray-core](https://github.com/XTLS/Xray-core)
- 🔍 **Парная заметка:** [[dpi-analysis-pipeline|Как DPI анализирует соединение: воронка проверок (от SYN до ML)]] — общий конвейер, в который вписана эта схема
- 🦎 **Парная заметка:** [[statistical-morphing-concept|Адаптивная мимикрия: статистический морфинг трафика]] — концепт, как *можно было бы* обойти поведенческий сигнал
- 🕵️ **Полевой кейс:** [[DPI/browser-ja4-fingerprint-block|Блокировка сайта по JA4-отпечатку браузера]] — «Сигнал 2» в дикой природе: сайт не открывается только в Chrome/Edge
- [[Zapret/about|Что такое обход DPI: VPN, Tor, DPI]]
- [[Zapret/premium|premium]] — рабочие конфиги
- [[Zapret/vless-sni|Список SNI для VLESS]]
- [[VLESS-localhost-protection-guide|Защита VLESS на стороне localhost]]
- [[VLESS-SOCKS5-vulnerability|Уязвимость VLESS + SOCKS5]]