Тестируем XHTTP

А расскажите, люди добрые, почему в представленных выше конфигах nginx проксирует в xray через grpc_pass, а не через proxy_pass? По-началу мне казалось, что если на входе h2, то и на выход логичнее отдавать h2. Грубо говоря, nginx при proxy_pass будет просто перекладывать пакеты из кармана в карман. Позже где-то встретил рекомендацию использовать grpc_pass, но не помню где. На первый взгляд это кажется избыточным из-за необходимости перепаковки. Электронный болван вторит моим рассуждениям и топит за proxy_pass. В скудной доке по xhttp grpc_pass упоминается только в плане подключения через CF… В чем плюс от grpc_pass при передаче в пределах одного сервака?

Лично я использую proxy_pass

а почему сервер на 8443? не очень понимаю.. мы же можем за nginx посадить все на 443.

PS По поводу grpc_pass , я скормил notebooklm repomixed репозитарий xray-core, xray exampels, xray-docs, pr5414, xhttp beyond reality 4118 и 4113
и он мне выдал следующее

1. Поддержка полноценного двунаправленного стриминга (h2c)

Режимы XHTTP stream-up и stream-one используют двунаправленные потоки данных в рамках HTTP/2. Начиная с версии Xray v25.1.30, режим stream-up был изменен так, что данные могут передаваться одновременно в обоих направлениях (как в stream-one).

  • Стандартный proxy_pass в Nginx по умолчанию работает через HTTP/1.1 при взаимодействии с бэкендом (xray). Протокол HTTP/1.1 не поддерживает такую структуру двунаправленного потока.

  • grpc_pass заставляет Nginx использовать протокол h2c (HTTP/2 без шифрования) для общения с локальным процессом Xray. Это позволяет сохранить все преимущества HTTP/2 (мультиплексирование, стриминг) на участке «Nginx → Xray» без лишних накладных расходов на TLS внутри сервера.

2. Производительность и эффективность

Источники прямо указывают, что производительность grpc_pass выше, чем у proxy_pass, при проксировании XHTTP-трафика. Это связано с тем, что Nginx при использовании grpc_pass оптимизирован для обработки долгоживущих потоковых соединений, характерных для gRPC и стриминговых режимов XHTTP.

3. Маскировка и совместимость с CDN

Многие режимы XHTTP (особенно stream-up) по умолчанию включают маскировку под gRPC (заголовок Content-Type: application/grpc), чтобы беспрепятственно проходить через Cloudflare и другие защитные системы.

  • Использование grpc_pass в Nginx гарантирует, что сервер будет корректно обрабатывать эти заголовки и специфическое поведение трафика, не пытаясь буферизировать его как обычный HTTP-запрос.

  • Для работы через Cloudflare в режимах stream-* использование gRPC-совместимого проксирования на стороне сервера (Nginx) и включение поддержки gRPC в панели CF являются обязательными условиями.

4. Решение проблемы «зависания» соединений

В обсуждениях отмечается, что стандартный proxy_pass может приводить к ошибкам (например, stream-up mode is not allowed или разрыву соединения), если клиент и сервер пытаются использовать стриминговые возможности, которые Nginx в режиме обычного проксирования не может корректно пропустить через себя. Переход на grpc_pass решает эти проблемы «из коробки».

Резюме: Хотя на первый взгляд передача h2 -> h2 через proxy_pass кажется логичной, на практике Nginx требует явного указания grpc_pass для активации поддержки h2c и корректной обработки двунаправленных потоков XHTTP. Это обеспечивает лучшую стабильность, высокую скорость и правильную обработку маскирующего gRPC-трафика даже в пределах одного сервера

У меня с caddy работал, у других с nginx.

Ну вот не понятно. Разные сетки разное сочиняют. При этом они могут переобуваться, когда начинаешь делать акцент на том, что nginx и xray работают в каскаде на одном vps. На счет xray examples тоже не всё безоблачно. Многие конфиги имеют многолетнюю давность. В общем, надо пробовать оба варианта и смотреть по факту.

В общем поставил рядом nginx. Все равно lighttpd в quic не умеет и раз уж udp 443 свободен, почему бы не поэкспериментировать с ним. Разумеется с nginx (grpc_pass) всё завелось. Сразу бросилась в глаза низкая скорость xhttp через quic (10 мбит/c) “за кардон”, но вряд ли виноват протокол, потому что на тестовом стенде в том же городе между двумя разными провайдерами скорость при такой же связке в 2,5 раза выше (и это внутри openvpn туннеля). При переходе на h2 (xhttp / tcp) скорость околотарифная, в райноне 90 мбит/c. (тариф сотка, роутер тоже сотка). Но низкая скорость для меня не главное (в принципе ютюб 1080p смотреть даже можно). Заметил такую особенность. При смене сети гаджетом (с вифи на 4G) связь не прерывывает или восстанавливается сразу. А вот когда обратно (из 4g в Wifi) соединяться может до 5 минут. Если вручную перезапустить v2rayng то сразу восстановится. Wifi точки разные, но в одном здании за одним натом (т.е. выходной IP один и тот же). Если между ними перемещаться в течение дня с ВЫКЛюченным 4G связь восстанавливается сразу, по факту подключения к WiFI. Думаю не надо говорить о том, что в режиме h2 (xhttp / tcp) проблем подобных нет. Видимо особенность работы quic либо баг самого клиента. Подобного эффекта на гаджете можно добиться находясь под Wifi если перезапустить nginx. При перезапуске xray или его остановке и последующем запуске через какое то время (при запущенном nginx) связь восстанавливается сразу. На v2rayn, т.е. комп через провод (при перезапуске nginx) связь по h3 тоже пропадает на 5 минут.

Настройки брал отсюда https://github.com/XTLS/Xray-examples/tree/main/VLESS-XHTTP3-Nginx

режим stream-one.

В v2rayng добавил это (кстати, спасибо, то что нужно)

В v2rayn ничего не добавлял, он всего лишь обертка для OpenVPN туннеля (который ходит через него), так что такое же одно соединение получается.

На 8443 я разворачивал этот тестовый стенд, не более того, не обращайте внимания.

Скорость quic лечится через увеличение http3_stream_buffer_size (по умолчанию 64k.) https://nginx.org/ru/docs/http/ngx_http_v3_module.html#http3_stream_buffer_size . Добавил в конфиг nginx:

http3_stream_buffer_size 1m;

Теперь 100 Мбит/с “за кардон“ полностью утилизируются. Явно это не предел, но быстрее канала для проверки у меня нет)

Вникнув в суть и наигравшись вдоволь пришел к такому конфигу:

nginx
server {
    listen unix:/run/nginx_http.sock ssl http2 proxy_protocol;
    server_name mysite.org;

    real_ip_header proxy_protocol; # Пробрасываем реальный ip клиента (если надо)
    set_real_ip_from unix:;

    keepalive_timeout 300s; # Тамаут keepalive
    keepalive_requests 2000; # Возможно придется увеличить при использовании maxConnections == 1

    http2_max_concurrent_streams 256; # Увеличиваем при низком maxConnections, чтобы в xray всё влезало в одну сессию

    ... # сертификаты

    location / {
        root /var/www/landing; # заглушка
        try_files $uri $uri/ =404;
    }

    location /xhttp-path {
        if ($host != "mysite.org") { return 404; } # по-детски отсекаем лишнее

        # Явно используем grpc для работы по h2c
        grpc_pass grpc://unix:/run/xray/xray_xhttp.sock;

        # Отключаем лимиты на объем данных
        client_max_body_size 0;

        # Таймауты для клиента
        client_body_timeout 300s;
        send_timeout 300s;

        # Технический буфер для заголовков (на случай если не хватит дефолтного)
        # grpc_buffer_size 16k;

        # Таймауты для grpc
        grpc_read_timeout 300s;
        grpc_send_timeout 300s;

        # Пробрасываем хидеры
        grpc_set_header Host $host;
        grpc_set_header X-Real-IP $remote_addr;

        # Отключаем буферизацию, "накопление" данных и т. д.
        tcp_nodelay on;
        tcp_nopush off;
    }
}

Понял, что тупанул в прошлый раз на счет proxy_pass и сделал неправильные выводы. Теперь всё встало на свои места. Для h2 → h2 действительно нужен grpc_pass. Попутно осознал, что очень долгий grpc_read_timeout/grpc_send_timeout - это путь в могилу (много где 1h в примерах). Будет копиться пачка мертвых соединений. Да и вообще это касается всех таймаутов. Субъективно выбрал 5 минут для всего. За это время хоть один keepalive да пролетит. В эксплуатации пока проблем не обнаружил. Клиент настроен на “имитацию” поведения браузера (maxConcurrency: “16-32”). Также пробовал конфиг для сибирской блокировки (maxConnections: 1) - работает без нареканий (но возможно потому, что сибирской блокировки у меня сейчас нет). Пропускная способность упирается в ограничиение моего интернет соединения. Нагрузка на vps околонулевая.

Как в v2rayN указать параметры extra, а конкретно xmux?
Я нашёл config в json, но он же перезапишется при любой правке из gui, или нет?

UPD: Опечатался, имел в виду v2rayN.
Отвечу сам: вбить настройки в мобильный клиент (v2rayNG), оттуда сделать экспорт в виде ссылки (extra экспортируется), и эту ссылку вставить в v2rayN. Проверил - extra импортируются корректно.

У вас голый хрей на сервере или панель какую используете?( Мне кажется грамотнее пушить конфиг с сервера через подписку чем вручную править на стороне клиента

Голый xray на сервере за nginx, который роутит по SNI
Пушить конфиг не умею пока :downcast_face_with_sweat:
Домена своего нет, под чужой маскировка

В старых версиях v2rayN параметры Extra были спрятаны под тремя точками. В новой версии всё проще.

Выбрать сервер. Нажать Ctrl+D (Редактировать). Найти раздел “Дополнительные параметры XHTTP (Extra)” и туда вставить нужный код. В нижней части окна не забыть нажать кнопку “Подтвердить”.

Класс, спасибо. Оказывается у меня “старая”, ей недели 3-4 уже)

кто-нибудь настраивал xhttp через CDN timeweb? Другие работают нормально а этот не могу завести.

Другие это какие?

cdnvideo, selectel

А в чем проблема? Какая ошибка? И какие режимы XHTTP + XMUX используете?

Хотел так же поинтересоваться, вас устроило cdnvideo или все неудобно?

У меня просто немного глаза на лоб вылезли от ценника в 8к на 5 террабайт.

Если selectel, cdnvideo, то xhttp работает, жалоб особо нет, тестирую уже так в плане качества, стабильности, а если timeweb то не работает вообще. Ошибок никаких пока не диагностировал по timeweb. Думал настройки xhttp крутить буду.

Рассчитывал, что тут люди уже тропу протоптали по timeweb и может что подскажут.

Имеется ввиду что происходит при подключении? RST? нет коннекта вообще даже с пингом? Домен не аллоцируется IP адресу? Нет подключения на inbound за nginx? utls блокировка?