XTLS-Vision vs XTLS-Reality in Xray-core: Architecture, Configuration, and Use Cases

XTLS-Vision is a VLESS protocol flow that adds post-handshake traffic obfuscation on top of standard TLS, while XTLS-Reality is a transport-layer security replacement that substitutes the entire TLS handshake with a fingerprint-mimicking protocol.

Both modes serve to evade deep packet inspection (DPI), but they operate at fundamentally different layers of the Xray-core network stack. Understanding their architectural differences is essential for selecting the right protection strategy for your proxy deployment.

Where Each Mode Lives in the Stack

XTLS-Vision: Application-Layer Flow

XTLS-Vision operates as a VLESS flow (xtls-rprx-vision), configured per-user rather than per-transport. The mode is activated by setting Flow: vless.XRV in a VLESS account configuration.

In proxy/vless/vless.go, the flow constant is defined:

const (
    XRV = "xtls-rprx-vision"
    // ... other flow constants
)

The actual traffic manipulation happens in proxy/proxy.go through VisionReader and VisionWriter. These wrappers:

  • Add randomized padding to TLS records
  • Monitor handshake completion via TrafficState
  • Switch to raw copy mode (zero-copy) once the TLS handshake finishes

The VLESS inbound handler in proxy/vless/inbound/inbound.go (around line 615) and outbound handler in proxy/vless/outbound/outbound.go (around line 392) inject these wrappers into the connection pipeline.

XTLS-Reality: Transport-Layer Replacement

XTLS-Reality replaces TLS entirely at the transport security layer. It is activated by setting SecurityType to reality.Config in StreamSettings, making it applicable to any proxy protocol (VLESS, Trojan, etc.).

The configuration structure is defined in transport/internet/reality/config.pb.go as a protobuf message:

type Config struct {
    Show        bool
    Fingerprint string
    ServerName  string
    PublicKey   []byte
    PrivateKey  []byte
    ShortId     []byte
    ShortIds    [][]byte
    Type        string
    Dest        string
    // ... additional fields
}

Core handshake logic resides in transport/internet/reality/reality.go, implementing:

  • uTLS fingerprinting to mimic popular browser TLS handshakes
  • X25519-ML-KEM for key exchange
  • Optional ML-DSA-65 certificate verification
  • Short ID derivation for server authentication

The TCP transport layer integrates Reality in transport/internet/tcp/hub.go (server) and transport/internet/tcp/dialer.go (client), which check SecurityType and wrap connections with Reality's Conn type.

Configuration Comparison

Enabling XTLS-Vision

Vision requires VLESS specifically and is configured per-user:

// VLESS inbound user with Vision flow
user := &protocol.User{
    Account: serial.ToTypedMessage(&vless.Account{
        Id:   "YOUR-UUID-HERE",
        Flow: vless.XRV,  // "xtls-rprx-vision"
    }),
}

The underlying transport still uses standard TLS (security: tls in JSON configuration). Vision adds its obfuscation layer after the TLS handshake completes.

Enabling XTLS-Reality

Reality operates at the transport layer and replaces TLS entirely:

// StreamSettings for Reality transport
stream := &internet.StreamConfig{
    ProtocolName: "tcp",
    SecurityType: serial.GetMessageType(&reality.Config{}), // Activates Reality
    SecuritySettings: []*serial.TypedMessage{
        serial.ToTypedMessage(&reality.Config{
            Show:        true,                    // Debug logging
            ServerName:  "www.google.com",        // SNI for handshake
            PublicKey:   publicKeyBytes,          // X25519 public key
            ShortId:     []byte{0x01,0x02,0x03,0x04},
            Fingerprint: "chrome",                // uTLS fingerprint
        }),
    },
}

Combining Both: Vision over Reality

The test suite in testing/scenarios/vless_test.go demonstrates combining both modes:

inbound := &core.InboundHandlerConfig{
    ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
        PortList: &net.PortList{Range: []*net.PortRange{net.SinglePortRange(443)}},
        StreamSettings: &internet.StreamConfig{
            ProtocolName: "tcp",
            SecurityType: serial.GetMessageType(&reality.Config{}), // Reality transport
            SecuritySettings: []*serial.TypedMessage{
                serial.ToTypedMessage(&reality.Config{
                    Show:        true,
                    Dest:        "www.google.com:443",
                    ServerNames: []string{"www.google.com"},
                    PrivateKey:  privateKey,
                    ShortIds:    shortIds,
                    Type:        "tcp",
                }),
            },
        },
    }),
    ProxySettings: serial.ToTypedMessage(&inbound.Config{
        Clients: []*protocol.User{
            {
                Account: serial.ToTypedMessage(&vless.Account{
                    Id:   userID.String(),
                    Flow: vless.XRV, // Vision flow on top of Reality
                }),
            },
        },
    }),
}

This configuration stacks Vision's post-handshake obfuscation on top of Reality's handshake replacement.

Key Implementation Files

File Purpose
proxy/vless/vless.go Defines XRV constant for Vision flow
proxy/proxy.go Implements VisionReader/VisionWriter with padding logic
proxy/vless/inbound/inbound.go Injects Vision wrappers for incoming connections
proxy/vless/outbound/outbound.go Injects Vision wrappers for outgoing connections
transport/internet/reality/config.pb.go Protobuf configuration for Reality
transport/internet/reality/reality.go Core handshake: uTLS, X25519-ML-KEM, ML-DSA-65
transport/internet/tcp/hub.go Server-side Reality integration
transport/internet/tcp/dialer.go Client-side Reality integration
testing/scenarios/vless_test.go End-to-end tests: TestVlessXtlsVision, TestVlessXtlsVisionReality

Summary

  • XTLS-Vision operates at the VLESS protocol layer, adding post-handshake traffic padding and raw-copy optimization. It requires VLESS and standard TLS underneath.

  • XTLS-Reality operates at the transport layer, completely replacing TLS with a handshake that mimics browser fingerprints and uses modern cryptographic primitives (X25519-ML-KEM, optional ML-DSA-65).

  • Both modes can be combined (Vision flow over Reality transport) for defense-in-depth, as demonstrated in the Xray-core test suite.

  • Configuration differs fundamentally: Vision is set per-user via Flow: "xtls-rprx-vision", while Reality is set per-stream via SecurityType: reality.Config.

Frequently Asked Questions

Can I use XTLS-Vision with protocols other than VLESS?

No. XTLS-Vision is implemented as a VLESS-specific flow constant (vless.XRV in proxy/vless/vless.go). The VisionReader and VisionWriter wrappers are injected by the VLESS inbound and outbound handlers. To use Vision-like features with other protocols, you would need to implement equivalent wrapping logic in those protocol handlers.

Does XTLS-Reality require VLESS?

No. XTLS-Reality is a transport-layer security replacement that works with any proxy protocol. In transport/internet/tcp/hub.go and dialer.go, the Reality configuration is checked via SecurityType regardless of the underlying proxy protocol. The test suite demonstrates Reality with VLESS, but the same transport configuration applies to Trojan and other protocols.

What happens when I combine XTLS-Vision with XTLS-Reality?

The two modes stack: Reality performs its custom handshake and fingerprint obfuscation at the transport layer, establishing an encrypted tunnel. Once established, the VLESS protocol activates Vision's VisionReader/VisionWriter wrappers, which add post-handshake padding and eventually switch to raw copy mode. This combination provides both handshake-level and traffic-level obfuscation, as validated in TestVlessXtlsVisionReality in testing/scenarios/vless_test.go.

Which mode offers better protection against DPI detection?

XTLS-Reality generally provides stronger protection against modern DPI systems because it eliminates the standard TLS handshake entirely, replacing it with a browser-fingerprinted handshake that appears as normal HTTPS traffic to passive observers. XTLS-Vision assumes the TLS handshake completes normally and only obfuscates subsequent traffic, which may still reveal proxy usage through handshake characteristics. However, Vision adds meaningful protection against traffic analysis attacks that examine payload sizes and timing patterns after the handshake completes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →