# VLESS UUID Authentication: How It Works and How It Differs From VMess

> Discover how VLESS UUID authentication works, its differences from VMess, and its simpler approach to client verification.

- Repository: [Project X Community, Not Porn-jet X Hub/Xray-core](https://github.com/XTLS/Xray-core)
- Tags: deep-dive
- Published: 2026-04-21

---

**VLESS authenticates clients using a plain-text UUID without any cryptographic handshake, while VMess combines UUID with security types in a full AEAD key exchange.**

The VLESS protocol in [XTLS/Xray-core](https://github.com/XTLS/Xray-core) takes a deliberately minimalist approach to authentication. Unlike its predecessor VMess, VLESS separates identity verification from encryption entirely. This article examines how VLESS UUID authentication works under the hood, referencing the actual source implementation, and contrasts it with VMess's more complex security model.

## How VLESS UUID Authentication Works

### The Core Architecture

VLESS authentication centers on the `MemoryValidator` struct in [`proxy/vless/validator.go`](https://github.com/XTLS/Xray-core/blob/main/proxy/vless/validator.go). When a VLESS inbound starts, each configured client becomes a `protocol.MemoryUser` whose `Account` field holds a UUID string. The validator stores these users in two concurrent maps:

- **`users`** — keyed by a *processed* UUID
- **`email`** — optional reverse-lookup by email address

```go
// proxy/vless/validator.go
type MemoryValidator struct {
    users   sync.Map // map[[16]byte]*protocol.MemoryUser
    email   sync.Map // map[string]*protocol.MemoryUser
}

```

### UUID Processing: Removing VMess Legacy

Before storage, VLESS processes every UUID through `ProcessUUID`:

```go
// proxy/vless/validator.go
func ProcessUUID(id [16]byte) [16]byte {
    id[6] = 0
    id[7] = 0
    return id
}

```

This function zeroes bytes 6 and 7 — the "version" and "variant" fields in standard UUID format. VMess historically used these bits to encode security type information. VLESS strips them to extract only the identity portion, making UUIDs purely identifiers without embedded security metadata.

### Authentication Flow

When a client connects, the VLESS inbound handler in [`proxy/vless/inbound/inbound.go`](https://github.com/XTLS/Xray-core/blob/main/proxy/vless/inbound/inbound.go) performs these steps:

```go
// Simplified excerpt from inbound.go handleRequest
func (h *Handler) handleRequest(ctx context.Context, conn net.Conn) error {
    // 1. Read exactly 16 bytes for client UUID
    var rawID [16]byte
    if _, err := io.ReadFull(conn, rawID[:]); err != nil {
        return err
    }

    // 2. Parse as UUID type
    clientID, err := uuid.ParseBytes(rawID[:])
    if err != nil {
        return err
    }

    // 3. Authenticate via validator
    user := h.validator.Get(clientID)
    if user == nil {
        return errors.New("failed to get VLESS user")
    }

    // 4. Access account metadata for flow/encryption decisions
    account := user.Account.(*vless.MemoryAccount)
    // ... continue with flow logic based on account.Flow, etc.
}

```

The `Get` method in `MemoryValidator` looks up the processed UUID:

```go
// proxy/vless/validator.go
func (v *MemoryValidator) Get(id uuid.UUID) *protocol.MemoryUser {
    u, _ := v.users.Load(ProcessUUID(id))
    if u != nil {
        return u.(*protocol.MemoryUser)
    }
    return nil
}

```

If no user matches, the connection aborts immediately with `failed to get VLESS user`. No retry, no fallback—authentication is strictly binary.

## VLESS vs VMess: Key Differences

| Aspect | VLESS | VMess |
|--------|-------|-------|
| **Authentication token** | Plain UUID (identity only) | UUID **plus** security type (aes-128-gcm, chacha20-poly1305, etc.) |
| **Handshake overhead** | None—server checks UUID and proceeds | Full KDF-based key exchange with AEAD encryption |
| **Header encryption** | Optional (`decryption` field for XOR/REALITY); not required for identity | Always encrypted via `SealVMessAEADHeader` in [`encoding/client.go`](https://github.com/XTLS/Xray-core/blob/main/encoding/client.go) |
| **Payload security** | Depends on optional flow (none, XTLS, REALITY); can be plaintext | Enforced by chosen security; invalid without it |
| **UUID storage** | `ProcessUUID` strips version bits; pure identity lookup | Raw UUID includes security encoding; lookup incorporates security type |
| **Reverse proxy support** | `Reverse` field in `MemoryAccount`; UUID-only gating | Similar capability but tied to security-specific UUID |

### VMess Authentication Deep-Dive

To understand why VLESS differs, examine VMess's approach in [`proxy/vmess/account.go`](https://github.com/XTLS/Xray-core/blob/main/proxy/vmess/account.go) and [`proxy/vmess/vmess.go`](https://github.com/XTLS/Xray-core/blob/main/proxy/vmess/vmess.go):

```go
// proxy/vmess/account.go — Account couples UUID with security
type Account struct {
    Id      string // UUID string
    AlterId uint32 // Legacy parameter
    Security string // "aes-128-gcm", "chacha20-poly1305", "auto", etc.
}

```

The VMess handshake in [`common/protocol/headers.go`](https://github.com/XTLS/Xray-core/blob/main/common/protocol/headers.go) and [`proxy/vmess/encoding/client.go`](https://github.com/XTLS/Xray-core/blob/main/proxy/vmess/encoding/client.go) derives session keys from both the UUID and security type:

```go
// Simplified from encoding/client.go
kdf := hmac.New(sha256.New, []byte("VMessBF"))
kdf.Write(id.Bytes())
kdf.Write(securityTypeBytes())
// ... derive keys for AEAD encryption

```

This KDF-based derivation means **VMess cannot authenticate without also negotiating encryption**. The UUID and security type are inseparable. VLESS's `ProcessUUID` explicitly removes this coupling, allowing the same UUID to work with any encryption flow (or none at all).

## Practical Configuration Examples

### Minimal VLESS Server Configuration

```json
{
  "inbounds": [
    {
      "protocol": "vless",
      "port": 443,
      "settings": {
        "clients": [
          {
            "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "flow": "xtls-rprx-vision",
            "encryption": "none"
          }
        ],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "tlsSettings": {
          "certificates": []
        }
      }
    }
  ]
}

```

Key points:
- `id` is the UUID passed verbatim to `MemoryValidator`
- `encryption:"none"` disables VLESS-level payload encryption (TLS/XTLS handles transport security)
- `decryption` in settings mirrors this for the server side

### VMess Equivalent for Comparison

```json
{
  "inbounds": [
    {
      "protocol": "vmess",
      "port": 443,
      "settings": {
        "clients": [
          {
            "id": "11223344-5566-7788-99aa-bbccddeeff00",
            "alterId": 64,
            "security": "aes-128-gcm"
          }
        ]
      }
    }
  ]
}

```

Here `security` is mandatory and tightly coupled to the UUID—no separate encryption layer selection exists.

## Summary

- **VLESS uses a plain UUID for authentication**, stripping version bits via `ProcessUUID` in [`proxy/vless/validator.go`](https://github.com/XTLS/Xray-core/blob/main/proxy/vless/validator.go) to create a pure identity lookup key.

- **No cryptographic handshake occurs**—the server reads 16 bytes, processes the UUID, and checks `MemoryValidator.users` via a concurrent map lookup.

- **Encryption is completely decoupled** from authentication, enabled optionally through `flow` (XTLS) or `streamSettings` (TLS/REALITY) rather than being embedded in the UUID.

- **VMess intertwines UUID and security**, requiring a KDF-based handshake and AEAD header encryption that binds identity to encryption method.

- **Performance impact**: VLESS's lightweight authentication reduces connection establishment overhead, particularly beneficial for high-concurrency scenarios and TLS-fingerprinting resistance via REALITY.

## Frequently Asked Questions

### Can VLESS work without any encryption?

Yes, though this is not recommended for production. Setting `encryption:"none"` and `security:"none"` transmits payloads in plaintext. The UUID authentication still occurs, but traffic is readable to any observer. Most deployments use TLS or XTLS for transport security instead.

### Why does VLESS process the UUID by zeroing bytes 6-7?

These bytes historically encoded VMess security type information. VLESS removes this coupling to treat UUIDs as pure identifiers. The `ProcessUUID` function in [`proxy/vless/validator.go`](https://github.com/XTLS/Xray-core/blob/main/proxy/vless/validator.go) ensures compatibility with standard UUID formats while eliminating legacy VMess metadata dependencies.

### Can the same UUID work with both VLESS and VMess?

Not simultaneously in a meaningful way. While both protocols accept standard UUID formats, VMess interprets the UUID as part of its security handshake, while VLESS processes it through `ProcessUUID`. A server configured for both protocols would need separate inbound configurations with distinct UUIDs or careful parameter mapping to avoid conflicts.

### What happens if a VLESS client sends an invalid UUID?

The connection aborts immediately. In [`proxy/vless/inbound/inbound.go`](https://github.com/XTLS/Xray-core/blob/main/proxy/vless/inbound/inbound.go), the handler calls `validator.Get(clientID)` and returns `errors.New("failed to get VLESS user")` if the lookup returns `nil`. No retry mechanism exists—invalid UUIDs result in silent connection termination without revealing server configuration details.