Skip to content

REALITY — Xray-core ​

REALITY is the Xray-developed alternative to TLS-with-a-public-cert: the server impersonates a real third-party website during the TLS handshake, falling back to a transparent proxy for unauthorized clients while upgrading authorized ones to a real proxy session. The handshake itself is indistinguishable from connecting to the impersonated site.

Configuration ​

Set in streamSettings.realitySettings when streamSettings.security is "reality". The same struct holds both server (inbound) and client (outbound) fields; only one half of the options is relevant on each side.

FieldTypeDefaultAllowed valuesDescription
masterKeyLogstring(unset)<file path>SSLKEYLOGFILE-style key dump for debugging. Don't enable on production.
showboolfalsetrue | falsePrint verbose REALITY handshake info to the error log on every connection. Diagnostic only — produces a lot of output.
targetjson.RawMessage(use dest)<host:port>Inbound — alias for dest. Set either, not both.
destjson.RawMessage(required)<host:port>Inbound — target of the camouflage handshake. The TLS Client Hello is proxied to this address when the client isn't a legitimate REALITY peer; it is the 'real' site that observers will see.
typestring(auto)tcp | unixInbound — backend protocol for dest. Inferred from the form of dest.
xveruint6400 | 1 | 2Inbound — PROXY-protocol version to prepend when forwarding to dest.
serverNames[]string(required)[<hostname>]Inbound — TLS SNI names the inbound accepts. Connections with a different SNI are forwarded to dest (the camouflage path).
privateKeystring(required)<base64 X25519 private key>Inbound — X25519 private key. Generate with xray x25519.
minClientVerstring(unset)<semver>Inbound — minimum Xray version a client is allowed to advertise. Older clients are rejected.
maxClientVerstring(unset)<semver>Inbound — maximum Xray version a client is allowed to advertise.
maxTimeDiffuint64(unset)<milliseconds>Inbound — maximum clock skew tolerated between client and server, in milliseconds. Wider than this rejects the handshake.
shortIds[]string(required)[<hex string>]Inbound — list of allowed short IDs (hex, even length, up to 16 chars). Empty entry "" allows clients that don't advertise a short ID. Each authorized user typically gets a unique short ID.
mldsa65Seedstring(unset)<base64 seed>Inbound — ML-DSA-65 post-quantum-signature seed. Pair with mldsa65Verify on clients.
limitFallbackUploadLimitFallback(unset)LimitFallbackInbound — rate-limit the upload direction once an authorized client transitions to the real proxy. Use to make REALITY indistinguishable from a slow legitimate site.
limitFallbackDownloadLimitFallback(unset)LimitFallbackInbound — same for the download direction.
fingerprintstringchromechrome | firefox | safari | edge | 360 | qq | ios | android | random | randomizedOutbound — uTLS client-hello fingerprint. Drives what the on-wire ClientHello looks like.
serverNamestring(required)<hostname>Outbound — SNI sent in the ClientHello. Must match one of the server's serverNames.
passwordstring(required)<string>Outbound — REALITY password (the X25519 public key plus an authentication tag). Generate with xray reality.
publicKeystring(unset)<base64 X25519 public key>Outbound — alternative to password; the server's X25519 public key directly.
shortIdstring""<hex string>Outbound — short ID announced to the server. Must be in the server's shortIds list. Empty string is allowed only if the server lists "".
mldsa65Verifystring(unset)<base64 verify key>Outbound — ML-DSA-65 verification key matching the server's mldsa65Seed.
spiderXstring(unset)/<path>Outbound — path-like spider hint that mimics legitimate browsing behavior during the camouflage handshake.

Source: infra/conf/transport_security.go:27-52 · pinned at v26.9.9 (52a412d)

Key generation ​

REALITY requires an X25519 keypair. Generate with the bundled Xray command:

sh
$ xray x25519
Private key: <base64-private-key>
Public key:  <base64-public-key>
Password:    <base64-password>

The Private key goes into the server's privateKey. Clients use either password (recommended — packs the public key with an auth tag) or publicKey directly.

For ML-DSA-65 post-quantum protection (optional):

sh
$ xray mldsa65
Seed:   <base64-seed>
Verify: <base64-verify-key>

Examples ​

Server (VLESS + REALITY impersonating www.cloudflare.com):

json
{
  "inbounds": [{
    "tag": "vless-reality",
    "listen": "0.0.0.0",
    "port": 443,
    "protocol": "vless",
    "settings": {
      "clients": [{ "id": "<UUID>", "flow": "xtls-rprx-vision" }],
      "decryption": "none"
    },
    "streamSettings": {
      "network": "tcp",
      "security": "reality",
      "realitySettings": {
        "show": false,
        "dest": "www.cloudflare.com:443",
        "xver": 0,
        "serverNames": ["www.cloudflare.com"],
        "privateKey": "<base64-private-key>",
        "shortIds": ["", "0123456789abcdef"]
      }
    }
  }]
}

Client matching the server above:

json
{
  "outbounds": [{
    "tag": "vless-reality",
    "protocol": "vless",
    "settings": {
      "address": "your.server.example",
      "port": 443,
      "id": "<UUID>",
      "flow": "xtls-rprx-vision",
      "encryption": "none"
    },
    "streamSettings": {
      "network": "tcp",
      "security": "reality",
      "realitySettings": {
        "fingerprint": "chrome",
        "serverName": "www.cloudflare.com",
        "password": "<base64-password>",
        "shortId": "0123456789abcdef"
      }
    }
  }]
}

Notes ​

  • dest is the camouflage target. Pick a real site whose TLS certificate is publicly trusted; observers see traffic flowing to this site for clients that aren't authorized. Common choices: www.cloudflare.com, www.microsoft.com, dl.google.com.
  • serverNames must contain the SNI clients will use. Server traffic arriving with an unlisted SNI is silently forwarded to dest — the fallback path that makes REALITY indistinguishable from the real site.
  • shortIds is per-user-or-group differentiation. Each authorized client uses one entry from the list. Add "" (empty) if you want to allow no-short-id clients; omit it for strict mode.
  • password is recommended over publicKey: it encodes the public key with an additional auth tag so a stolen public key alone is not sufficient to authenticate.
  • limitFallbackUpload / limitFallbackDownload cap the throughput of authorized clients after the REALITY upgrade. Setting these to match the real upstream's bandwidth makes REALITY harder to detect through bandwidth fingerprinting.
  • show: true produces verbose logs of every REALITY handshake — only use for testing.

Cross-core notes ​

  • sing-box splits the schema cleanly into InboundRealityOptions and OutboundRealityOptions, with the camouflage target nested inside a handshake sub-block. See REALITY — sing-box.
  • mihomo uses two structs too: outbound RealityOptions (just public-key + short-id) and inbound RealityConfig (server side with dest, private-key, server-names, short-id, …). See REALITY — mihomo.

Source: infra/conf/transport_security.go:27-52 · v26.9.9 (52a412d)

Core Tutorial by Argsment