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 viaSecurityType: 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →