A Bit of Background
The classic way to use VLESS (or similar proxy protocols, including those paired with XTLS-Reality) is straightforward: the client opens a direct connection to a proxy server running on a VPS somewhere. That worked fine for years.
Then things changed. In several countries — Russia being the most visible example — censors began blocking or throttling entire IP ranges belonging to well-known hosting providers. Elsewhere, detection systems started flagging single IP addresses that receive unusually high traffic volumes. Direct connections stopped being reliable.
The obvious workaround was to route proxy traffic through a CDN. Most implementations used the WebSocket transport. But WebSocket-over-CDN has two real problems:
- It has a recognizable signature (we won't spell it out here — no need to make DPI operators' jobs easier).
- Only a limited set of CDNs actually support WebSocket proxying, and you'd rather not be restricted to that short list.
The Tor project hit this problem first and invented the meek transport for its bridges. Meek tunnels data through long sequences of ordinary HTTP request/response pairs, which means it works through essentially any CDN. The briefly revived V2Ray project adopted meek as well.
Meek solves the CDN compatibility problem but introduces two new ones baked into its design:
- It's slow. The request/response model is effectively half-duplex, and the constant round-trips add enormous overhead.
- It's expensive. The flood of GET/POST requests per second gets you kicked off free CDN tiers quickly, and on paid tiers it generates painful invoices.
The XRay team looked at meek and asked a good question: what if you split sending and receiving onto two separate connections? That idea became the SplitTunnel transport, which evolved into what we now call XHTTP.
Two Things to Clear Up First
Before going further, two clarifications that save a lot of confusion:
XHTTP is a transport, not a protocol. It's typically paired with VLESS, but in principle it could carry other protocols too.
XHTTP was designed for CDN use, but it doesn't require a CDN. You can absolutely use it for direct client-to-server connections, and doing so still brings real advantages.
The Three Modes
XHTTP operates in one of three modes. The choice determines both performance and compatibility.
1. packet-up
The slowest of the three — but still dramatically faster than meek — and also the most compatible. It works with almost any web server or CDN.
Under the hood: one connection carries server-to-client traffic, while client-to-server traffic is split across many short-lived connections.
A note on terminology: when we say "connection" here, we don't strictly mean a TCP connection. We mean an HTTP request/response session. Many of these sessions can share a single TCP connection. But in all cases, the TCP connections used for inbound and outbound traffic will be different.
2. stream-up
Faster than packet-up, but more picky about what it runs through. Only works with certain web servers (or ones specifically configured for it).
Uses one long-lived connection for client→server traffic and another long-lived connection for server→client traffic.
3. stream-one
The odd one out. This is the only mode that doesn't split inbound and outbound traffic — both directions share a single connection. Functionally, it's close to plain VLESS with an HTTP header (XRay used to have exactly that transport).
Stream-one only passes through Nginx with the grpc_pass directive, or through Cloudflare with the gRPC feature enabled. Worth noting: gRPC isn't actually being used here. It's just a decoy — a shape that makes Nginx and Cloudflare leave the traffic alone.
Why XHTTP Is Worth the Trouble
Here's what you get by adopting XHTTP.
1. HTTPS or QUIC
You can connect to the server over HTTPS as usual, or switch to QUIC by explicitly setting ALPN to h3 on the client. QUIC tends to perform better on flaky networks, and it's a useful escape hatch when HTTPS is blocked but whoever set up the blocking forgot about QUIC.
2. Authentic server fingerprints over TLS 1.2
This one deserves some unpacking.
A TLS "fingerprint" is the combination of subtle characteristics in how a server implements TLS — its quirks, its behaviors, the order of things. An XRay server and a real Nginx or Caddy instance leave different fingerprints, and that difference is enough to out a proxy to a sufficiently motivated inspector.
The standard defense is XTLS-Reality in "steal from yourself" mode: your proxy masquerades as a real web server that happens to be running on the same host. The catch is that XTLS-Reality requires TLS 1.3 — and censors have been caught blocking TLS 1.3 to popular hosting ranges specifically because of this.
XHTTP sidesteps this by letting you use TLS 1.2, with a perfectly authentic fingerprint, because a real Nginx is the thing listening on port 443. XRay sits behind it.
3. CDN flexibility (including domain fronting)
XHTTP works through CDNs that don't support WebSocket or gRPC. And because some CDNs still permit domain fronting (MiraclePtr covered this technique in depth on Habr), you can take masquerading further than XTLS-Reality alone allows.
The picture looks like this: when you connect, you present the SNI of some unrelated popular domain, you get that domain's real certificate back, and you're hitting the actual IP address that domain resolves to — along with thousands of other people in the country who happen to be visiting that site at the same time. You're hiding in a crowd, not pretending to be one person.
4. Browser dialer
This solves the fingerprint problem from the client side. XRay (and similar tools) use the uTLS library to imitate popular browsers' TLS fingerprints, but imitation is never perfect.
With browser dialer, the flow changes: you load a small script in your real browser, and your XRay client routes through that browser instead of connecting directly. The proxy sees a fingerprint and behavior that are indistinguishable from a real browser — because they are a real browser.
5. Asymmetric connection paths
This is the most interesting one. In packet-up and stream-up modes, your data is already split across separate inbound and outbound connections. That alone makes pattern analysis and TLS-in-TLS detection significantly harder.
But here's the thing: nothing requires those two connections to be identical. XRay lets you configure them independently. Some of what that enables:
- Send outbound over QUIC, receive inbound over HTTPS.
- Outbound over IPv6, inbound over IPv4.
- Outbound and inbound pointed at different IPv6 addresses.
- Two connections going to two entirely different servers, different hosting providers, different countries.
- One direction direct, the other through a CDN.
- Different domains for each direction.
Mix and match as creatively as you want. Every variation you add makes analysis and blocking more expensive for whoever is watching.
Points Worth Knowing
A handful of technical details that will save you time:
- XMUX — a new multiplexing algorithm — ships alongside XHTTP. Nothing revolutionary, but nothing tricky either. The GitHub write-up covers it adequately if you run it through an auto-translator.
- XHTTP is not compatible with XTLS-Vision. Protection against TLS-in-TLS detection comes from the multiplexing (XMUX) and the inbound/outbound splitting instead.
- XHTTP does work with XTLS-Reality. When you combine them, stream-one mode is selected by default unless you override it.
- XHTTP is under active development. Keep the XRay versions on the client and server matched. Version mismatches produce weird, hard-to-debug failures or plain silence.
- Sing-box-based clients don't support XHTTP. XRay-based clients do. v2rayN and v2rayNG both handle it without issue.
- Example configurations for XHTTP with XRay client, server, and Nginx live in the xray-examples repository.
The Bigger Picture
A closing thought worth taking seriously.
Over the past year, censorship systems have been losing ground on the protocol-detection front. Spotting traffic by indirect signals or deep statistical analysis is possible, but it requires both budget and expertise. The money may be there; the expertise less so. In response, censors have shifted to blunter measures with heavier collateral damage: blocking HTTPS to popular hosting providers wholesale, blocking TLS 1.3 to those ranges, blocking entire CDNs, blocking anything they can't identify, throttling SSH connections after a certain data threshold, and temporarily (from fifteen minutes to several days) tightening restrictions on individual subscribers whose traffic tripped some DPI heuristic.
The implication is that the next generation of circumvention probably shouldn't be about inventing ever-cleverer protocols and masking techniques. It should be about exploiting the weaknesses of the blocking mechanisms themselves — making them progressively more expensive to operate and progressively harder to maintain.
One concrete example: domestic traffic usually isn't filtered in most of these systems. Which suggests a layout where the first hop from the client is a cheap in-country VPS — reached however you like, even with an old Shadowsocks config, or with Reality masquerading as a local website — and from there the traffic continues to the real proxy abroad.
And if a hosting provider starts running DPI of its own and causing problems, the reverse-proxy/bridge/portal patterns that MiraclePtr has written about come into play, because return traffic typically isn't filtered either, and filtering it would cost orders of magnitude more compute.
One more hint while we're on the topic of CDNs: they don't have to be foreign ones. Think about it.
PS: Deploying a VLESS + XHTTP setup on a fresh VM? Doprax ProVM instances give you clean IPs, 1 Gbps ports, unlimited traffic, and European locations with low latency to the regions that need this kind of setup most. Spin one up in under a minute and you're ready to configure.
