Skip to content

REALITY — mihomo

mihomo supports REALITY on both sides. Outbound uses reality-opts: embedded in the proxy entry; inbound uses reality-config: on the listener.

Outbound — reality-opts

FieldTypeDefaultAllowed valuesDescription
public-keystring(required)<base64 X25519 public key>Server X25519 public key. Generated by the server admin via mihomo's `generate reality-keypair` or Xray's `xray x25519`.
short-idstring(unset)<hex string>Short ID announced to the server. Must match one of the server's `short-id` entries (or be empty if the server lists `""`).
support-x25519mlkem768boolfalsetrue | falseAdvertise post-quantum X25519MLKEM768 hybrid key exchange. Server must support the matching curve.

Source: adapter/outbound/reality.go:13-18 · pinned at v1.19.27 (5184081)

Inbound — reality-config

FieldTypeDefaultAllowed valuesDescription
deststring(required)<host:port>Camouflage target — the legitimate site whose TLS handshake is proxied for unauthorized connections. The site's cert must be valid; mihomo verifies it during handshake forwarding.
private-keystring(required)<base64 X25519 private key>Server X25519 private key.
short-id[]string[][<hex string>]List of allowed short IDs (hex, even length, ≤ 16 chars). Include `""` to allow clients that don't advertise a short ID.
server-names[]string(required)[<hostname>]Acceptable SNI values. Connections with a different SNI are forwarded to `dest` (camouflage fallback).
max-time-differenceint0 (no limit)<seconds>Maximum tolerated clock skew between client and server, in seconds. 0 disables the check.
proxystring(direct)<proxy name>Route the camouflage-fallback traffic through a named outbound proxy instead of dialing `dest` directly. Useful when the camouflage site can't be reached from the server.
limit-fallback-uploadRealityLimitFallback(unset)RealityLimitFallbackRate-limit the camouflage-fallback upload direction. Use to make REALITY traffic indistinguishable from a slow legitimate site.
limit-fallback-downloadRealityLimitFallback(unset)RealityLimitFallbackRate-limit the camouflage-fallback download direction.

Source: listener/inbound/reality.go:5-15 · pinned at v1.19.27 (5184081)

limit-fallback-upload / limit-fallback-download

FieldTypeDefaultAllowed valuesDescription
after-bytesuint640<bytes>Apply the rate cap only after this many bytes have flowed through. 0 caps from the first byte.
bytes-per-secuint640 (no limit)<bytes>Sustained bandwidth cap in bytes per second. 0 disables the cap.
burst-bytes-per-secuint640 (=bytes-per-sec)<bytes>Burst bandwidth cap. 0 keeps the same as `bytes-per-sec` (no burst).

Source: listener/inbound/reality.go:17-21 · pinned at v1.19.27 (5184081)

Key generation

mihomo includes a key generator:

sh
$ mihomo generate reality-keypair
PrivateKey: <base64-private-key>
PublicKey:  <base64-public-key>

Xray's xray x25519 produces the same format (both use X25519 in base64-RawURLEncoding) — keys are interchangeable across the three cores.

Examples

Outbound — VLESS with REALITY:

yaml
proxies:
  - name: vless-reality
    type: vless
    server: your.server.example
    port: 443
    uuid: <UUID>
    flow: xtls-rprx-vision
    tls: true
    servername: www.cloudflare.com
    client-fingerprint: chrome
    reality-opts:
      public-key: <base64-public-key>
      short-id: 0123456789abcdef

Inbound — VLESS listener with REALITY server-side:

yaml
listeners:
  - name: vless-reality-in
    type: vless
    listen: 0.0.0.0
    port: 443
    users:
      - username: alice
        uuid: <UUID>
        flow: xtls-rprx-vision
    reality-config:
      dest: www.cloudflare.com:443
      private-key: <base64-private-key>
      short-id:
        - ''
        - 0123456789abcdef
      server-names:
        - www.cloudflare.com
      max-time-difference: 60
      limit-fallback-upload:
        bytes-per-sec: 8388608
        burst-bytes-per-sec: 16777216
      limit-fallback-download:
        bytes-per-sec: 12582912

Notes

  • support-x25519mlkem768 on the outbound enables the hybrid post-quantum key exchange added in 2024. Servers built against a matching curve list will accept it; older servers see a regular X25519 handshake.
  • The inbound's proxy: field routes the camouflage fallback through another mihomo outbound. This is unique to mihomo — useful when the server can't directly reach the camouflage site (sing-box and Xray always direct-dial dest).
  • limit-fallback-upload.after-bytes is the warm-up: the cap kicks in only after this many bytes. Use a small value (~100 KB) so the camouflage handshake itself isn't throttled.
  • Setting max-time-difference: 60 (one minute) is generous enough for servers without NTP. Setting it to 0 disables clock-skew checks entirely — convenient for development, risky for production.

Cross-core notes

  • Xray-core keeps everything in a single REALITYConfig. The camouflage target is dest; uTLS fingerprint sits on the same struct as fingerprint. See REALITY — Xray-core.
  • sing-box nests REALITY inside the embedded tls: block on both sides. The camouflage target is a sub-block called handshake (which embeds ServerOptions + DialerOptions). See REALITY — sing-box.

Source: adapter/outbound/reality.go:13-18 · v1.19.27 (5184081)

Core Tutorial by Argsment