RouteHardenHire us
← Back to blog
Anonymity Engineering··9 min read

Xray Reality vs WireGuard: when to use which

Two protocols, two threat models. WireGuard hides what's in the pipe. Reality hides that there's a pipe at all.

WireGuard and Xray Reality both encrypt traffic, but they are not interchangeable tools and the people who treat them as alternatives usually pick the wrong one. The difference comes down to one question: does your adversary care about the contents of your traffic, or the fact that your traffic exists?

WireGuard is a tunnel. It assumes the network is a normal network — packets flow, ports work, UDP is delivered — and its job is to make sure no one in the middle can read what's inside. Reality, the transport layer that ships with Xray, assumes the network is hostile to tunnels. Its job is to make sure the network can't even tell a tunnel is there. That distinction shapes everything below.

What WireGuard is good at

WireGuard is the right answer most of the time. It's small (the entire kernel module is a few thousand lines), fast (it lives in the kernel and doesn't context-switch on every packet), and the configuration is a single ini file you can read on a phone screen. The mature tooling, the default-on Curve25519 + ChaCha20-Poly1305 cryptography, and the boring-by-design protocol all make it the obvious pick for the threat models most people actually have:

  • A passive ISP or workplace network that can see traffic exists but can't decrypt it.
  • Geo-restriction bypass for streaming and content licensing.
  • Replacing a corporate VPN appliance that costs five figures a year.
  • Site-to-site links between your own infrastructure.

In all of these, the network is willing to forward UDP packets, isn't fingerprinting transport protocols, and doesn't care whether you look like a VPN. WireGuard's tradeoff — a distinctive UDP signature in exchange for ruthless simplicity — is a bargain.

It also happens to perform extraordinarily well. A modern WireGuard endpoint will saturate gigabit links on a $5 VPS without breaking a sweat, and the kernel implementation means you don't pay a per-packet userspace tax the way you do with most competitors.

What Xray Reality is good at

Reality solves a different problem entirely. On a network that actively inspects, classifies, and blocks based on traffic shape — TLS fingerprints, SNI strings, JA3/JA4 hashes, active probing of suspicious endpoints — WireGuard's UDP signature gets dropped at the edge and you don't even get a tunnel up. You need traffic that doesn't merely encrypt its contents but actively masquerades as something the network is willing to forward.

Reality's trick is to mimic the TLS handshake of a real, unrelated upstream website. Your client connects to your server on TCP/443, presents a TLS ClientHello whose SNI points at a popular HTTPS site (www.microsoft.com, www.bing.com, anything heavily used and unlikely to be blocked), and the server uses a hidden authentication byte inside that ClientHello to recognize legitimate clients. If a probe shows up — a censor's automated scanner trying to figure out what kind of server lives at this IP — Reality transparently proxies the connection through to the real upstream site. The probe sees Microsoft's actual TLS certificate, served by Microsoft's actual TLS stack, because that's what's on the other end of the proxied bytes.

The threat models Reality is built for:

  • Active deep packet inspection that classifies and drops non-allowlisted protocols.
  • TLS fingerprinting and SNI-based filtering.
  • Networks that probe suspicious endpoints to confirm whether they're proxies before blocking.
  • Anonymity research where you don't want a server's existence to be discoverable from traffic patterns alone.

The cost: Reality is more complex to deploy, runs in userspace, and gives up WireGuard's raw throughput. You pay for the masquerade.

Decision matrix

PropertyWireGuardXray Reality
TransportUDP (single signature)TCP/443 (mimics target HTTPS site)
Fingerprint surfaceDistinctive UDP shape, easy to classifyMirrors a real upstream's TLS handshake
PerformanceKernel-fast, line-rateUserspace, lower ceiling
CryptoCurve25519 / ChaCha20-Poly1305, fixedTLS 1.3 over the chosen mimicry target
Setup complexityLow — one config, one keypairHigher — keypair, short ID, SNI choice, upstream selection
Multi-client configTrivial (peer entries)Subscription file or per-client links
Active-probe resistanceNone — endpoint is obviously a tunnelStrong — falls through to real upstream
Best forTrusted networks, performance-boundHostile networks, fingerprint-bound

The matrix isn't really about features; it's about what you're hiding from. If the network is willing to forward you packets and only wants to read them, WireGuard wins. If the network actively decides what to forward based on what packets look like, you need Reality.

How Reality actually works

The full protocol details are in the XTLS repo, but the high-level picture is worth understanding because it explains why Reality is hard for an adversary to defeat without breaking the actual web.

A normal TLS 1.3 handshake exposes the SNI in the ClientHello, hands back a certificate from the server, and proceeds with key exchange. Reality piggybacks on this by:

  1. Client mimicry. The client constructs a ClientHello that is byte-identical (in fingerprint terms) to the one a real browser would send to the chosen target site — the same cipher list, same extensions in the same order, same ALPN, same JA3/JA4. The server's IP appears, to anyone watching, to be a server hosting that target site.
  2. Hidden authentication. Inside the ClientHello, in a field that looks like normal TLS data, the client embeds an X25519-derived authentication value. The Reality server checks this value before deciding what to do with the connection.
  3. Authenticated path. If the auth value verifies, the server completes a TLS 1.3 handshake using its own keypair (no certificate authority required — this is part of why there's no separate Reality cert to leak), and the tunnel is up.
  4. Probe fall-through. If the auth value is missing or wrong — meaning the connection is an unauthenticated probe, an active scanner, or a curious censor — the server transparently proxies the entire TCP stream to the real upstream site. The prober sees the real Microsoft (or Bing, or whatever target was chosen) responding with a genuine certificate signed by a real CA. Nothing distinguishes the proxy from a misconfigured CDN edge.

A minimal server-side realitySettings block looks like:

"streamSettings": {
  "network": "tcp",
  "security": "reality",
  "realitySettings": {
    "show": false,
    "dest": "www.example.com:443",
    "xver": 0,
    "serverNames": ["www.example.com"],
    "privateKey": "<x25519-private-key>",
    "shortIds": ["<random-hex>"]
  }
}

The two operationally important choices are dest and serverNames. The target site has to be reachable from your client's network (otherwise the masquerade falls apart on the very first packet), it has to be a real HTTPS host (or probes won't get a coherent fall-through response), and it shouldn't be a domain the censor associates with VPN infrastructure. Pick a target that's so widely used that blocking it would have unacceptable collateral damage.

System time matters here too: TLS 1.3 verifies handshake timestamps, and a Reality server with a clock more than about a minute off will silently fail handshakes with no useful client error. Run chrony. The few engineers who skip this step learn about it the hard way.

When to combine them

Reality terminates on a VPS. VPS IPs come from datacenter ranges that streaming services, payment processors, and fraud-detection vendors flag as VPN traffic regardless of what protocol you used to get there. A Reality tunnel is great at hiding that you have a tunnel, but the moment your traffic egresses, the destination still sees a Hetzner or DigitalOcean IP.

The architectural fix is to chain the Reality endpoint to a residential-IP outbound — the Reality server accepts the masqueraded TLS, decrypts, and then forwards the user's actual traffic through a residential proxy provider. The destination sees a Comcast or BT or NTT consumer IP; the network in front of the user sees what looks like a connection to a popular tech giant's website.

The pattern in the Xray config:

"outbounds": [
  {
    "tag": "residential",
    "protocol": "http",
    "settings": {
      "servers": [
        {
          "address": "<residential-proxy-host>",
          "port": 0,
          "users": [{"user": "<u>", "pass": "<p>"}]
        }
      ]
    }
  },
  { "tag": "direct", "protocol": "freedom" },
  { "tag": "block", "protocol": "blackhole" }
]

A routing rule then sends matched traffic out the residential tag. This is overkill for most use cases, but it's the right architecture if your problem is both "the network in front of me hates tunnels" and "the network behind me hates datacenter IPs." Pick whichever proxy provider you trust on operational grounds; the architectural pattern is what matters.

Picking one

Match the tool to the threat model.

  • If your network is willing to forward UDP and you just don't want anyone reading the contents — WireGuard. It's faster, simpler, and one fewer moving part is one fewer thing to debug at 2am.
  • If your network actively classifies and drops protocols, fingerprints TLS, or probes suspicious endpoints — Reality. The complexity buys you something WireGuard can't offer: traffic that looks like nothing.
  • If your destination cares about IP reputation and the path in cares about traffic shape — Reality plus a residential outbound. Two problems, two layers.

The bad failure mode is the engineer who has a passive ISP and a Netflix problem reaching for Reality because it sounds more capable. You'll fight a complicated, brittle stack to solve a problem WireGuard handles in twenty minutes. The other bad failure mode is the engineer in a hostile-network environment running raw WireGuard and wondering why the tunnel comes up for ninety seconds and then dies. WireGuard's UDP signature is famous; networks that care about it will find it.

Pick based on what your adversary actually does, not what sounds impressive in a slide deck. WireGuard is for "private over a normal pipe." Reality is for pretending the pipe doesn't exist.

If you're standing up either of these in production and want a second pair of eyes on the threat model or the deployment, that's the kind of thing we do at /services.