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_internet.go:772-797 · pinned at v26.6.1 (94ffd50)

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_internet.go:772-797 · v26.6.1 (94ffd50)

Core Tutorial by Argsment