Monitoring

VPN monitoringUptime monitoringIntegrations

Platform

AI assistantsAPI and CLISecurity

More

PricingFree testerDocsBlog Log in

Guides

VLESS protocol explained: how it works, Vision, REALITY, configs

VLESS is a lightweight, stateless proxy protocol from the Xray project. The client sends a short header (a version byte, a 16-byte UUID, the command and the destination) and then the data; the server checks the UUID against its list and forwards the traffic. VLESS does not encrypt by default and does not depend on the clock, so in practice it always runs inside TLS or REALITY. An optional post-quantum VLESS Encryption arrived in the stable Xray-core v25.9.5 release.

ST Muntaha10 min read

VLESS is the protocol behind every vless:// link that a 3x-ui, Marzban or Hiddify panel hands out, and it is simpler than its reputation. This post explains what VLESS does on the wire, what it leaves to other layers, and how the pieces around it (the Vision flow, transports, REALITY) fit together. It ends with a minimal server and client config. Versions I checked against: Xray-core v26.10.31 and sing-box 1.14.4, the current releases on 1 November 2026.

What is the VLESS protocol?

VLESS is a proxy protocol: it tells a server "connect me to this address and port", proves who is asking, and then carries the data. Xray-core is the reference implementation, and sing-box implements it too. The official VLESS configuration docs describe it in one line: "a stateless, lightweight transport protocol".

Three properties explain almost everything about it:

  • Stateless. Each connection authenticates on its own. There are no sessions to keep, and the server needs no clock: unlike VMess, VLESS works even when the client's time is wrong.
  • UUID authentication. Each user has an ID. It is normally a UUID, but Xray also accepts any string of less than 30 bytes and maps it to a UUIDv5. xray uuid makes a random one, and xray uuid -i "some text" shows the UUID for a string.
  • No encryption of its own, by default. VLESS leaves confidentiality to the layer below it, which is TLS or REALITY. Inside TLS, a second layer of encryption would only cost CPU.

Is VLESS a VPN?

No. VLESS carries TCP connections and UDP traffic for applications, one connection at a time. It does not create a network interface, assign the client an address in a private network, or route IP packets the way WireGuard or OpenVPN do. The server only sees "connect to this host and port".

Client apps make it feel like a VPN. They run a TUN interface on the device, take each connection that enters it, and send that connection through VLESS. For a user the result looks the same. For an operator the difference matters: there is no tunnel subnet to route, and the server's outbound and DNS settings decide where traffic goes.

What does a VLESS request look like on the wire?

The VLESS protocol spec is short. A request is:

Size Field Notes
1 byte Protocol version Xray-core sends 0
16 bytes User ID The UUID as raw bytes
1 byte Addons length M 0 when there are no addons
M bytes Addons Protobuf; carries the flow, such as Vision
1 byte Command 1 = TCP, 2 = UDP, 3 = Mux, 4 = reverse proxy
2 bytes Port Destination port
1 byte Address type 1 = IPv4, 2 = domain, 3 = IPv6
S bytes Address Destination address
X bytes Request data The application's first bytes

The response is even smaller: the same version byte, an addons length, the addons (usually none), then the data.

There is no MAC, no timestamp and no padding in the header. That is why VLESS is cheap to run, and why it must never cross the internet without TLS or REALITY around it: anyone on the path could read the UUID and the destination of every connection.

The server checks the 16 ID bytes against its user list. If the ID is unknown, Xray logs invalid request user id and either closes the connection or, if fallbacks are configured, passes it on to another service such as a web server. Fallbacks are a VLESS and Trojan feature in Xray; they let one port serve both the proxy and a normal website.

Does VLESS encrypt traffic?

Not unless you turn on VLESS Encryption. In a normal config the server sets "decryption": "none" and the client sets "encryption": "none". The value cannot be left empty. A server config without it fails with VLESS settings: please add/set "decryption":"none" to every settings.

Since Xray-core v26.7.11, the client also refuses to send plain VLESS to a public address. If security is none and VLESS Encryption is off, the config fails with vless without TLS or other encryption is prohibited unless the server address is a private IP or domain. Plain VLESS is still allowed to private addresses, for example between two machines in one data centre.

VLESS Encryption itself first shipped in the Xray-core v25.8.29 pre-release, and v25.9.5 was the first stable release with it (PR #5067). The value is a dot-separated string that starts with mlkem768x25519plus, a handshake that combines ML-KEM-768 and X25519, and xray vlessenc generates a matching server and client pair. A few facts to know before using it:

  • Xray's own transport docs say it protects the VLESS payload but does not give the ordinary HTTPS appearance of TLS or REALITY. It is not meant to replace them for censorship resistance.
  • A server with decryption set cannot use fallbacks.
  • sing-box 1.14.4 has no encryption option in its VLESS outbound, so links that use VLESS Encryption work only in Xray-based clients for now.

How is VLESS different from VMess?

VMess is the older V2Ray protocol. It authenticates with the UUID plus the current time and always encrypts its own payload. VLESS dropped both.

VLESS VMess
Authentication UUID, sent as-is inside TLS or REALITY UUID combined with the time
Clock dependency None Client and server within 120 seconds
Own encryption None by default; optional VLESS Encryption Always, unless set to none
XTLS Vision flow Yes No
Fallbacks Yes No
Status in Xray-core v26 Current Startup warning: deprecated

The last row is recent. Since v26.1.23, Xray-core prints The feature VMess (with no Forward Secrecy, etc.) is deprecated, not recommended for using and might be removed at startup for any VMess inbound or outbound, and points users to VLESS Encryption. VMess still works, but new deployments have no reason to pick it.

What is the xtls-rprx-vision flow?

Vision is the flow that VLESS carries in its addons. It does two things. First, it adds random padding to the inner TLS handshake (the handshake between the user's browser and the website), because the record sizes of TLS inside TLS form a recognisable pattern. Second, once the inner connection reaches TLS 1.3 application data, Vision stops wrapping it: the data is already encrypted, so Xray copies it directly instead of encrypting it a second time. On Linux this can use the kernel's splice, so the data never passes through Xray's memory.

The constraints are strict:

  • Vision works only on the RAW transport with TLS or REALITY. With VLESS Encryption enabled it works on any transport. Anywhere else, Xray fails with XTLS only supports TLS and REALITY directly for now.
  • Client values are xtls-rprx-vision and xtls-rprx-vision-udp443. The first one blocks UDP to port 443 (QUIC), so browsers fall back to HTTPS over TCP, which Vision can handle. The second one lets QUIC through, for apps that fail without it.
  • The server's user and the client must agree. A client without flow to a Vision user gets account ... is rejected since the client flow is empty, and a client with flow to a user without it gets account ... is not able to use the flow xtls-rprx-vision.

Which transports can VLESS use?

VLESS sits on top of an Xray transport, set in streamSettings. The transports that matter for VLESS are:

  • RAW (raw, formerly called tcp): the VLESS bytes go straight onto a TCP connection. The only one that supports Vision without VLESS Encryption.
  • XHTTP (xhttp, formerly SplitHTTP): VLESS over HTTP requests, over HTTP/1.1, HTTP/2 or HTTP/3. Passes CDNs and reverse proxies, and works with REALITY.
  • gRPC (grpc): VLESS over HTTP/2 gRPC streams.
  • WebSocket (ws) and HTTPUpgrade (httpupgrade): an HTTP/1.1 upgrade, then a stream. Both pass most CDNs.
  • mKCP (mkcp): a reliable stream over UDP.

REALITY works only with RAW, XHTTP and gRPC. Current Xray-core prints a deprecation warning for gRPC, WebSocket and HTTPUpgrade and recommends XHTTP instead; they still work. Two older transports are gone: the HTTP/2 transport (h2/http) and QUIC were removed in 2024, and a link with type=http fails on current cores.

What is REALITY, in brief?

REALITY replaces TLS on the server side. The server holds an X25519 private key. The client holds the matching public key (Xray now calls it password; in links it is pbk) and one of the server's short IDs.

The client sends a normal-looking TLS 1.3 Client Hello, with a browser fingerprint from the uTLS library and the SNI of a real website that is not yours. Inside the Client Hello's session ID field it hides a small encrypted block: its Xray version, the current time and its short ID. The server derives a key from the client's key share and its own private key and tries to decrypt the block. If it decrypts and the short ID is on the list, the server finishes the TLS handshake with a temporary certificate the client can verify. If not, the server forwards the whole connection to the real website (the target), and the visitor sees that site's genuine certificate.

That last behaviour is the point: a scanner that probes your server sees someone else's real website. The REALITY README sets the minimum for a target: TLS 1.3 and HTTP/2, a domain that is not used only for redirects, and a site outside the censoring country.

One current compatibility note: REALITY servers on Xray-core v26.9.9 and later also require the X25519MLKEM768 post-quantum key share in the Client Hello. Current Xray clients with the default chrome fingerprint send it. sing-box clients currently do not, and they fail against these servers with reality verification failed.

A link is vless://UUID@host:port?parameters#name. The parameters carry everything else: the transport (type), the security layer (security), the TLS or REALITY fields, and transport fields such as path. Here is a link for VLESS over WebSocket with TLS, behind a normal domain:

vless://[email protected]:443?type=ws&encryption=none&security=tls&sni=vpn.yourdomain.com&host=vpn.yourdomain.com&path=%2Fapi%2Fws#de-fra-ws

And one for RAW with REALITY and Vision:

vless://[email protected]:443?type=tcp&encryption=none&security=reality&sni=www.example.com&fp=chrome&pbk=DVm-D2KuvBWxtDYfTW6rxE-9YY7q53V9HQ413NryNw0&sid=48047fad2a02cd24&spx=%2F&flow=xtls-rprx-vision#de-fra-01

Every field, what it maps to in a config, and how to test a link layer by layer are in how to check a VLESS key.

What does a minimal Xray VLESS server and client config look like?

This is VLESS with REALITY and Vision on port 443, a common setup on panels such as 3x-ui. First, generate the values on the server:

xray uuid
xray x25519
openssl rand -hex 8

xray x25519 prints three lines. PrivateKey goes in the server config. Password (PublicKey) goes to clients, as password in a config or pbk in a link. REALITY does not use the third line, Hash32. The openssl command gives you a 16-character short ID. The values below are examples only: generate your own, and never reuse keys from a blog post.

The server, for example /usr/local/etc/xray/config.json on an install made with the official script:

{
  "log": { "loglevel": "warning" },
  "inbounds": [
    {
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "vless",
      "settings": {
        "clients": [
          { "id": "aafd62da-d5aa-406c-985d-17cc48f142fd", "flow": "xtls-rprx-vision", "email": "user1" }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "raw",
        "security": "reality",
        "realitySettings": {
          "target": "www.example.com:443",
          "serverNames": ["www.example.com"],
          "privateKey": "WIjetb6W4iVMPUNHFgXWPNIwcHmyAZmYKnjpnN5nCl0",
          "shortIds": ["48047fad2a02cd24"]
        }
      }
    }
  ],
  "outbounds": [
    { "protocol": "freedom", "tag": "direct" },
    { "protocol": "blackhole", "tag": "block" }
  ],
  "routing": {
    "rules": [
      { "ip": ["geoip:private"], "outboundTag": "block" }
    ]
  }
}

Replace www.example.com with the site you borrow, in both target and serverNames. The routing rule stops users from reaching private addresses through your server, such as its own internal network; the release zip and the install script both include the geoip.dat file it needs.

The client is the same in reverse: the UUID and flow in settings, and the REALITY public half in realitySettings:

{
  "protocol": "vless",
  "settings": {
    "address": "vpn.yourdomain.com",
    "port": 443,
    "id": "aafd62da-d5aa-406c-985d-17cc48f142fd",
    "encryption": "none",
    "flow": "xtls-rprx-vision"
  },
  "streamSettings": {
    "network": "raw",
    "security": "reality",
    "realitySettings": {
      "serverName": "www.example.com",
      "fingerprint": "chrome",
      "password": "DVm-D2KuvBWxtDYfTW6rxE-9YY7q53V9HQ413NryNw0",
      "shortId": "48047fad2a02cd24"
    }
  }
}

Check both files with xray run -test -c config.json before you restart anything. It prints Configuration OK. or the exact field that is wrong. Two version notes: current docs call network by its new name, method, and v26.10.31 accepts both; the flat client settings block needs Xray-core v25.9.11 or later, and older cores use the vnext form.

The server says warning for the log level on purpose. When something fails, raise it to info for a while: that level shows rejected UUIDs and the reason for every rejected REALITY handshake.

Full disclosure: TunnelHQ monitors VLESS servers by doing what the client config above does, from outside your network. You paste the vless:// link, and it completes the REALITY or TLS handshake, authenticates and sends real requests through the tunnel every 1 to 5 minutes depending on plan. VLESS monitoring explains the check, and every plan starts with a 14-day free trial.