Repository navigation
[feature request] XHTTP Support packet down & retransmission #4846
Description
Problem
A few weeks ago, a new method of blocking (especially on mobile networks) was introduced in Russia by the censor.
It works as follows. So, if:
- The client connects to the server via TCP using HTTPS and TLS 1.3 (Reality actually mimics HTTPS with TLS 1.3);
- The IP address of the server is “suspicious” (it is located outside (i.e. not in Russia), is part of the subnet AS which is owned by foreign data centers like Hetzner, Digital Ocean, etc.);
- Within one TCP connection (not within one HTTP request/response - this is important) the data size received from the server to the client is more than ≈15-20KB (the value varies depending on the provider) — the MAIN thing.
This connection is "frozen" (i.e. TCP packets from the server within this connection stop arriving after the specified limit on the size of transmitted data has been reached). It is curious to note that, as a rule, the censor (under the pretence of a response from the server) does not send, for example, an RST packet - instead, it simply “freezes” the TCP connection (from the client's point of view), and thus the client simply waits for an error on the timeout of waiting for a response from the server and everything stops working.
IMPORTANT: It doesn't matter whether it's "legitimate" HTTPS traffic or whether someone is mimicking it. They don't pay attention to that now.
Do I have these limitations?
You can check your "home" ISP with the RU :: TCP 16-20 checker from this repository. Works right from your browser, VPN should be disabled during the check.
Proposed Solution
The good news is that if you fragment the responses from the server into several ≈15-20KB data bundles (if it is large enough) in different TCP connections, everything will work fine. This is the option I propose to add to Reality (where the size of the data bundle will be set by a parameter), which would be compatible with VLESS.
It is important not to confuse this with fragmentation of TCP packets within one connection or splitting of transmitted data into several HTTP requests/responses (in one connection) - this is different and it won't help.
By the way, the same rules will probably apply to client-to-server traffic (if not already done by the censor) - so the fragmentation option described is worth adding on both server and client. And support for fragmented data, of course.
Notes
Yes, it will work noticeably slower than a single connection (especially if we're going to upload/download a large file). For example, it would take ~2560 TCP connections to download a 50MB file - that's a lot and quite suspicious. But it's clearly better than not working at all. Anyway, this issue will help to publicize the problem and maybe someone will think of better solutions.
Some links to related issues:
- https://blog.cloudflare.com/russian-internet-users-are-unable-to-access-the-open-internet/
- BurstObservatory: add option to set http method for burst check #4835 (comment)
- [Russia] Censor has a new method of blocking net4people/bbs#490
- https://ntc.party/t/09062025-%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%86%D0%B8%D1%8F-%D0%BF%D0%BE-%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B5-cloudflare-ovh-hetzner-digitalocean/17013
Activity
hyperion-cs commentedon Jun 27, 2025
❗ For those who are in Russia and suffer from the problem (VLESS/Reality): at the moment the only way I know of to get around this problem (without implementing the functionality above) is to use an intermediate server in Russia, which itself is not analyzed by the censor under the “new” rules. Probably, large data centers connected directly to the main communication channels ("магистральные каналы связи") would be suitable.
This intermediate server should be configured as a transparent TCP proxy to the server with VLESS/Reality (which will already be out of the country, abroad). For this purpose you can use iptables, HAProxy, etc. Clients will have to change the host to the intermediate server (leave the other settings the same).
upd: TLS 1.2 is also blocked (sometimes there is a different data size limit per TCP connection). However, SSH works without restrictions (it seems you can use SSH tunnel or mimic it).
Fangliding commentedon Jun 28, 2025
20 kb就双向丢弃和被屏蔽了没有区别 这没有意义再去捞了
你们需要的可能是发明一个只会在一条tcp连接上传输20kb的新协议而不是修改现有协议 不过即使是这样效率也会低到令人发指
RPRX commentedon Jul 6, 2025
最近有点无聊,想给 XHTTP 的下行也搞出上行那种流量聚合了,反正客户端 hub 可以大量复用上行的代码Fangliding commentedon Jul 6, 2025
虽然有packet down是好的 但是xhttp现在的上行机制至少也可以利用h2 mux h1.1 keepalive
20kb只有xhttp默认最大post长度的2% 太小了 1MB的数据传下来要切成50个小部分还得通过50个独立的tcp连接接收 更别说xhttp无法从错误中恢复 这50个tcp连接任何一个被稍微阻断连接直接断开
RPRX commentedon Jul 6, 2025
其实我只是想给 XHTTP 加上聚合不同线路下载速度的能力,就像 mptcp,mekya 那些,但非常简单,具体效果几何需要实测,就像我说过的,如果单连接限速不明显,或者本来就是多线程下载,那就没什么提升,不过叠不同线路会不会对单线程有惊喜,可以试试
就叫“多路径下行”吧,不光 TCP,还能叠上 UDP,实现这个 issue 所述的目标只是顺带的,
如果不小心实现了 PLUX 也是顺带的RPRX commentedon Jul 6, 2025
还有,一旦有了自定义的下行包装格式,HTTP 下行就可以保活了,不会被 CDN 100s 不活跃掐断,
虽然我觉得这 100s 挺合理的还有玩得更花的,总是有人提“迁移现有连接”,以后再看看要不要补上 ACK 机制吧
所以 XHTTP 终究还是不需要 mux.cool 了,XUDP 除外[-]`Reality` payload fragmentation over different TCP connections feature (especially important in Russia)[/-][+][feature request] XHTTP Support packet down & retransmission[/+]zzlinwq commentedon Jul 12, 2025
支持大佬 大佬比起马斯克有过之而无不及
RPRX commentedon Jul 31, 2025
https://t.me/projectXray/4386271
Sebulba46 commentedon Aug 4, 2025
So I'm trying to replicate idea with proxy server in Russia but I want to connect to proxy server through wireguard and then route traffic using iptables to the vless client config accepting connections on port 1080.
My wireguard setup:
xray config:
Using this setup i can connect to the proxy server through wireguard, but forwarding is not working. And xray itselft is working fine: i can curl https://medium.com. Could you please help me?
gfw-killer commentedon Sep 27, 2025
the lack of Retransmission in XHTTP is a real issue
iopq commentedon Dec 12, 2025
Why are you using socks?
39 remaining items
XHTTP: Add opt-in downlink resume
Medium1992 commentedon Sep 13, 2026
I tested both approaches this thread discusses for the idle downlink cut.
Keepalive over Mux.Cool works and is simple — the server just sends the existing SessionStatusKeepAlive frame — but it adds a small downstream frame every few seconds, a periodic pattern that isn't there in normal traffic.
Resume (§6, prototyped in #6755) avoids that. Opening a fresh request after one ends is ordinary HTTP — browsers do it constantly — so a reconnect looks like normal traffic, not a signature, and it happens only reactively, after the CDN has already cut the stream, so there is no fixed interval to lock onto.
On the "can't guarantee no byte loss" concern from #6755: it doesn't apply to the case this targets. The cut fires on an idle stream, so at that moment the server has sent nothing the client hasn't already received — the replay buffer is essentially empty, there is nothing to lose. Byte loss could only happen on a full-rate download outrunning the window, which is never when an idle cut fires. The two cases don't overlap.
@RPRX @Fangliding worth reconsidering resume for this?
RPRX commentedon Sep 14, 2026
@Fangliding 话说那些重复开的 issue
不知道又是哪些 AI 智能体越狱了,直接删掉吧,群里的刷屏也清一下Fangliding commentedon Sep 14, 2026
这是蓄意破坏 故意复制的以前的 我开了交互限制但是没权限ban人和删帖(
RPRX commentedon Sep 14, 2026
不知道谁这么无聊,那还是别删了吧留存证据,估计每次都注册新号,ban 了也没用,把群里的刷屏删一下吧Fangliding commentedon Sep 14, 2026
又来了 这次是发评论 我限制到仅允许贡献者了 看着办吧
mKCP transport: Add `cwndMultiplier`; Apply unaggressive strategy by …
RPRX commentedon Sep 17, 2026
#6755 (comment)
GIVLSE commentedon Sep 26, 2026
Comment for XTLS/Xray-core #4846
I have additional field evidence from a reproducible XHTTP
packet-up/ HTTP/2 / CDN failure on Xray 26.7.28.This looks related to #4846 and to the downlink failure mode discussed in PR #6755, but there is one important difference in our reproduction:
Environment
packet-upReproduction workload
A playback-style test repeatedly downloads fixed 3,125,000-byte HTTPS segments through the tunnel.
The first segments complete normally:
Then stalls begin.
Examples from failed requests:
All of these requests:
Across the failed playback requests, the received body before timeout was approximately:
The median missing tail was only about 37 KiB.
Overall playback result:
Separate long-session result
A separate 30-minute stability test through the same tunnel produced:
Xray stayed alive during the whole run.
Origin-side correlation
We also collected bounded packet/socket evidence on the origin and correlated client failure windows with the actual outbound target sockets opened by Xray.
For 15 / 15 captured failure windows:
So in these failures the destination server had already responded and the response bytes had already reached the origin-side Xray process, while the client was still waiting and later timed out.
This strongly suggests that the failure happens after the origin receives the target response, somewhere in the XHTTP downlink / intermediary / client delivery path.
Client Xray log
During the stalls, the client repeatedly logs:
After the application times out and closes its local socket, follow-up errors such as:
also appear, which look like consequences of the timeout rather than the original cause.
Why this may be relevant beyond idle-downlink cuts
The failure mode discussed in #4846 / #6755 appears very close to what we see, especially the
INTERNAL_ERROR; received from peererrors and the fact that the origin has already received the target response.However, our playback case shows something more specific:
That may mean either:
packet-updownlink path through some CDN edges.I can provide a sanitized full client/server config, Xray debug logs, and the detailed per-failure trace evidence if that would help isolate the failure further.
Medium1992 commentedon Sep 26, 2026
@GIVLSE As a stopgap, you could try the server build from my fork: https://github.com/Medium1992/Xray-core-fork/releases/tag/v26.9.9-mux-ka-4 (Xray 26.9.9,
linux/amd64andlinux/arm64). The release notes describe the setup.It lets the origin send a Mux.Cool KeepAlive frame into an XHTTP downlink that has been idle for a while (
packet-upandstream-up), so only the origin needs this build. The client can stay on stock Xray, but it must havemuxenabled, since the keepalive is a Mux.Cool frame. SetmuxKeepAliveSecsbelow the idle timeout your CDN applies to the downlink.If the tails still stall with a keepalive tuned to your CDN and mux on the client, then idling is most likely not the cause, which would fit your observation that the stream is cut while data is still flowing.
XHTTP: Add opt-in downlink resume
XHTTP: Add opt-in downlink resume
Waretame commentedon Oct 2, 2026
Following RPRX's direction in #6755 (#6755 (comment)): packet-down frames carried inside stream-down, header with optional session id excerpt, optional seq, type (padding/data), length, payload, and the first target is downlink keep-alive only.
Is there a fuller write-up of the design somewhere, or is that comment the spec so far? Two things I'm not sure about:
Meanwhile I'll start from that comment as is: a minimal implementation, frames in stream-down, keep-alive only, off by default, with knobs for period / length range / padding content, and adjust once there's a clearer picture.
mKCP transport: Add `cwndMultiplier`; Apply unaggressive strategy by …
izislesar commentedon Oct 15, 2026
I implemented the packet-down frame format from RPRX's design comment in #6755 (comment) and opened #7159.
In short:
h1 / h2 / h3 are covered, since the framers sit on the response body rather than on one transport. stream-one's single response body is framed the same way.
It does not address the 15-20 KB per TCP connection cut from this issue yet; that needs the bigger packet-down uses later. Testing and review are welcome, especially from people with CDNs in front of the origin, since the target there is exactly the inactivity timer.