← Back to library
Protocols Theory

XHTTP transport: when and why

XHTTP doesn't solve the same problem Reality does. Reality hides you from blocking by handshake; XHTTP hides you from blocking by IP, tucking the node behind a CDN. I explain what this transport is, how it differs from WebSockets, and when to reach for it. Configs — in the paired practice.

Go to practice

This material is about engineering your own infrastructure and is educational in nature. Complying with the laws of your own jurisdiction is your responsibility.

A different axis of protection

Let me lay it out first so there's no confusion. DPI chokes a VPN along three axes: handshake/SNI, traffic behavior, IP. Reality and Vision close the first two. But when your IP simply gets blocked — the address poisoned, the host's subnet banned — no handshake masquerade will help: the packets physically don't reach the node. Here you need a different protection — to hide the node behind someone else's big, trusted IP. That's the job of a CDN. And for your VPN to ride through the CDN, you need a transport the CDN lets pass as ordinary web traffic. XHTTP is exactly that.

So XHTTP isn't a competitor to Reality but a different tool for a different trouble. Often they're even combined: XHTTP as the transport plus Reality as the handshake masquerade.

What XHTTP is and where it came from

XHTTP is a transport over ordinary HTTP. It used to be called SplitHTTP, then was renamed. The idea: pack the VPN stream into a set of ordinary HTTP requests and responses, so that to any intermediate node (a CDN first and foremost) it looks like a regular HTTP exchange — GETs, POSTs, familiar headers, nothing exotic.

It came to replace the WebSocket transport, which was long the standard for passing behind a CDN, and it replaced it for a reason. WebSocket has inborn ailments: it requires a connection upgrade (that same Upgrade: websocket), holds a single long-lived connection, and not all CDNs like it — some cut or time out long upgraded connections. XHTTP sidesteps these problems: it doesn't upgrade the connection into a websocket, but works as a stream of ordinary HTTP requests that the CDN handles routinely, with no special mode.

Why it in particular plays nice with CDNs

CDNs are designed to cache and proxy HTTP. Anything that looks like ordinary web traffic they pass readily; anything exotic — with caveats, or they cut it outright. XHTTP deliberately mimics an ordinary HTTP exchange, so it passes through CDNs that gag on websockets.

Separately about the modes. XHTTP has three ways to push data upstream (from client to server), and choosing between them is a trade-off of "reliability versus latency":

  • auto — the transport chooses for itself, a sensible default for most cases.
  • packet-up — more reliable behind finicky CDNs that misbehave with long streams; the data goes as separate requests, which the CDN digests more calmly.
  • stream-up — lower latency, the stream is more continuous, but more demanding on how the CDN holds the connection.

In practice I start with auto, and if drops surface behind a specific CDN — I switch to packet-up. That's the first thing worth trying when "it won't come up behind a CDN".

An important distinction: XHTTP has no Vision

A point people coming from TCP-Reality trip over. On XHTTP flow isn't used — there's no Vision here and there shouldn't be. Vision is a specific thing for raw TCP, where you have to manually break the double-TLS pattern. XHTTP has a different model: the stream is blurred by the very nature of the HTTP transport, the data is packed into requests, and no separate flow is needed. Set flow on an XHTTP inbound and you'll get a broken or incorrect config. Just leave the field empty.

Two usage scenarios

XHTTP comes in two wrappings, and this needs to be distinguished:

XHTTP + Reality (direct). The XHTTP transport, with the handshake masqueraded by donor Reality with no cert of your own. This is for when you want the pros of the XHTTP transport but connect directly to the node, not through a CDN. The same stealth as Reality, plus the resilience of the HTTP transport.

XHTTP + TLS (behind a CDN). Honest TLS on your own domain — what you need for fronting behind Cloudflare or another CDN. TLS is terminated by your domain or by the CDN itself, and the traffic goes as ordinary HTTPS to a "site" that is actually your node behind the front. This is the main scenario for bypassing IP blocking.

When to pull XHTTP from reserve

I keep XHTTP not as the primary channel but as a survival layer that turns on as the situation demands:

  • The IP or subnet got blocked. Reality won't save you — the handshake is fine, but the packets don't arrive. You move behind a CDN onto XHTTP-TLS, and the client connects to the CDN domain, not to your address.
  • They're cutting TCP specifically. If the region chokes the TCP profile and UDP-Hysteria isn't an option, XHTTP behind a CDN is the workaround.
  • A hidden reserve for auto-swap. One visible primary channel (TCP-Reality) plus XHTTP in the subscription as a hidden config: the primary goes down — the client switches to XHTTP automatically.

Separately about the cost: behind a CDN you add latency (an extra hop through the front, usually fractions of a second). So XHTTP-behind-a-CDN isn't something you put "on everyone by default", but something you reach for when the direct connection has stopped working.

What's next

You've got the gist: XHTTP is about bypassing IP blocking through a CDN, not about handshake masquerade; it has no Vision; and it comes in two wrappings — direct with Reality and behind a CDN with TLS. How to bring up XHTTP over Reality by hand — target, path, modes, verification — is in the paired practice "Setting up XHTTP over Reality".

Next guide Setting up XHTTP over Reality → Article unclear or something off? Message me and I will help or fix it. @notrealvpn →
This material is educational and covers network-infrastructure engineering. You are responsible for complying with the laws of your jurisdiction.