Skip to content

ECH — mihomo

mihomo exposes ECH as the ech-opts block on every proxy adapter that speaks TLS. The same 3-field struct is used across VLESS, VMess, Trojan, Hysteria2, TUIC, AnyTLS, and the obfs-flavored Shadowsocks variants.

ech-opts

FieldTypeDefaultAllowed valuesDescription
enableboolfalsetrue | falseTurn ECH on for this proxy. When false, the rest of the block is ignored.
configstring(auto-discover)<base64 ECHConfigList>Pinned ECH config list (base64-encoded, standard padding). When unset, mihomo queries the HTTPS DNS record of the server name to auto-discover the config.
query-server-namestring(server name)<hostname>Override the domain used for the ECH HTTPS-record query. Defaults to the proxy's `sni` / `servername` / `server`. Useful when the cover hostname differs from the actual target.

Source: adapter/outbound/ech.go:12-17 · pinned at v1.19.27 (5184081)

The same struct is reused as the obfuscation-plugin ECH block (under shadowsocks plugin-opts for v2ray-plugin and gost-plugin), which is why every field has both proxy: and obfs: tags.

Examples

Outbound — VLESS with opportunistic ECH:

yaml
proxies:
  - name: vless-ech
    type: vless
    server: example.com
    port: 443
    uuid: <UUID>
    tls: true
    servername: example.com
    network: tcp
    ech-opts:
      enable: true

Outbound — pinned ECH config (base64):

yaml
proxies:
  - name: vless-ech-pinned
    type: vless
    server: example.com
    port: 443
    uuid: <UUID>
    tls: true
    servername: example.com
    ech-opts:
      enable: true
      config: <base64 ECHConfigList>
      query-server-name: cover.example.com

Notes

  • mihomo's config value is standard base64 (with padding), not base64-url-safe. If you have a URL-safe-base64 value, convert it first.
  • Auto-discovery uses mihomo's ProxyServerHostResolver — the resolver used for resolving proxy hostnames. That resolver in turn obeys the top-level dns: block, including any DoH/DoT settings.
  • Inbound mihomo listeners that support ECH (VLESS / VMess / Trojan / Hysteria2 / TUIC / AnyTLS) accept the ECH key bundle in the ech-key listener field — see the respective inbound pages. There is no consolidated ech-config inbound block.

Cross-core notes

  • Xray-core exposes ECH as fields directly on tlsSettings (echServerKeys, echConfigList, echForceQuery, echSockopt). See ECH — Xray-core.
  • sing-box uses a nested tls.ech block on inbounds and outbounds with snake_case names. See ECH — sing-box.

Source: adapter/outbound/ech.go:12-17 · v1.19.27 (5184081)

Core Tutorial by Argsment