TUIC — mihomo
mihomo supports both TUIC v4 (token-based) and TUIC v5 (UUID+password) on both sides. There are two ways to declare an inbound: the standard listeners: entry, or the top-level tuic-server block.
Outbound
Entry under proxies: with type: tuic. Embeds BasicOption.
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
name | string | (required) | <string> | Unique proxy name. |
server | string | (required) | <host> | Upstream server hostname or IP. |
port | int | (required) | <port> | Upstream server port. |
token | string | (unset) | <string> | Legacy TUIC v4 token. Mutually exclusive with `uuid`+`password` (v5). |
uuid | string | (required for v5) | <UUID> | User UUID (TUIC v5). |
password | string | (required for v5) | <string> | User password (TUIC v5). |
ip | string | (unset) | <host> | Override the destination IP after resolution. Used when SNI / cert is for a different name than the actual server IP. |
heartbeat-interval | int | 10000 | <milliseconds> | QUIC heartbeat interval. **Milliseconds**, not seconds. |
alpn | []string | [h3] | h3 | ALPN list. TUIC requires HTTP/3 ALPN. |
reduce-rtt | bool | false | true | false | Use 0-RTT handshake on reconnect. |
request-timeout | int | 8000 | <milliseconds> | Server-response timeout for the connect frame. Milliseconds. |
udp-relay-mode | string | native | native | quic | How UDP packets are tunneled. `native` (datagrams) is faster; `quic` (per-packet streams) works through datagram-blocking middleboxes. |
congestion-controller | string | cubic | cubic | new_reno | bbr | bbr_meta_v1 | bbr_meta_v2 | QUIC congestion controller. `bbr` is mihomo's v2 BBR. `bbr_meta_v1`/`bbr_meta_v2` keep the older variants for cross-compatibility. |
disable-sni | bool | false | true | false | Omit the SNI extension from the TLS Client Hello entirely. |
max-udp-relay-packet-size | int | 1252 | <bytes> | Maximum UDP payload size before fragmentation. |
fast-open | bool | false | true | false | Send the proxy-request frame on the same flight as the QUIC handshake. |
max-open-streams | int | 100 | <int> | Maximum concurrent streams per QUIC connection. |
cwnd | int | 32 | <int> | Initial congestion window in packets. Defaults to 32 (`transport/tuic/common/congestion.go:17-19`). |
bbr-profile | string | (unset) | default | aggressive | BBR tuning profile when `congestion-controller: bbr`. |
skip-cert-verify | bool | false | true | false | Disable TLS verification (test only). |
fingerprint | string | (unset) | <SHA256 hex> | Pin the server's TLS certificate fingerprint. |
certificate | string | (unset) | <PEM file path> | Client certificate (mTLS). |
private-key | string | (unset) | <key file path> | Private key for `certificate`. |
recv-window-conn | int | 0 | <bytes> | Per-connection initial receive window. 0 keeps the quic-go default. |
recv-window | int | 0 | <bytes> | Per-stream initial receive window. 0 keeps the quic-go default. |
disable-mtu-discovery | bool | false | true | false | Skip path-MTU discovery; useful on networks that drop probes. |
max-datagram-frame-size | int | 1252 | <bytes> | Maximum QUIC datagram frame size. |
sni | string | (server) | <SNI> | TLS Server Name Indication. |
ech-opts | ECHOptions | (disabled) | ECHOptions | Encrypted Client Hello configuration. |
udp-over-stream | bool | false | true | false | Use the UDP-over-stream framing instead of QUIC datagrams. |
udp-over-stream-version | int | 1 | 1 | Framing version. Only 1 is defined today. |
Source: adapter/outbound/tuic.go:34-69 · pinned at v1.19.27 (5184081)
Inbound (under listeners)
Entry under listeners: with type: tuic. Embeds BaseOption.
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
token | []string | [] | [<string>] | Legacy TUIC v4 token list. Empty disables v4. |
users | map[string]string | {} | {<uuid>: <password>} | TUIC v5 user table. Keys are UUIDs; values are passwords. |
certificate | string | (required) | <PEM file path> | TLS server certificate. |
private-key | string | (required) | <key file path> | TLS private key. |
client-auth-type | string | (none) | no-client-cert | request-client-cert | require-any-client-cert | verify-client-cert-if-given | require-and-verify-client-cert | Mutual-TLS client-auth mode. |
client-auth-cert | string | (unset) | <PEM file path> | CA bundle accepted as client roots. |
ech-key | string | (unset) | <ECH config> | Encrypted Client Hello material. |
congestion-controller | string | cubic | cubic | new_reno | bbr | bbr_meta_v1 | bbr_meta_v2 | Server-side QUIC congestion controller. |
max-idle-time | int | 0 | <milliseconds> | Idle timeout for the QUIC connection. 0 keeps the QUIC default. |
authentication-timeout | int | 1000 | <milliseconds> | Maximum wait for the client to send an authentication frame, in milliseconds. |
alpn | []string | [h3] | h3 | ALPN list offered during the TLS handshake. |
max-udp-relay-packet-size | int | 1252 | <bytes> | Maximum UDP payload size accepted from clients. |
cwnd | int | 32 | <int> | Initial congestion window in packets. |
bbr-profile | string | (unset) | default | aggressive | BBR tuning profile. |
mux-option | MuxOption | (disabled) | MuxOption | sing-style multiplex settings. |
Source: listener/inbound/tuic.go:12-29 · pinned at v1.19.27 (5184081)
Inbound (top-level tuic-server)
The top-level tuic-server block is an alternative to declaring a TUIC inbound under listeners. It exists for legacy reasons; for new configs prefer the listener form (it composes naturally with the rest of the inbound model).
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
enable | bool | false | true | false | Master switch for the top-level TUIC inbound. When false, the rest of the block is ignored. |
listen | string | 127.0.0.1:0 | <host:port> | Listen address. Same syntax as elsewhere in mihomo. |
token | []string | [] | [<string>] | TUIC v4 token list. |
users | map[string]string | {} | {<uuid>: <password>} | TUIC v5 user table. |
certificate | string | (required) | <PEM file path> | TLS server certificate. |
private-key | string | (required) | <key file path> | TLS private key. |
congestion-controller | string | cubic | cubic | new_reno | bbr | bbr_meta_v1 | bbr_meta_v2 | Server-side QUIC congestion controller. |
max-idle-time | int | 0 | <milliseconds> | QUIC idle timeout. |
authentication-timeout | int | 1000 | <milliseconds> | Client auth-frame timeout. |
alpn | []string | [h3] | h3 | ALPN list. |
max-udp-relay-packet-size | int | 1252 | <bytes> | Maximum UDP payload size. |
cwnd | int | 32 | <int> | Initial congestion window. |
Source: config/config.go:323-336 · pinned at v1.19.27 (5184081)
Examples
Outbound — TUIC v5:
proxies:
- name: tuic-v5
type: tuic
server: example.com
port: 443
uuid: a3482e88-686a-4a58-8126-99c9df64b7bf
password: <password>
udp-relay-mode: native
congestion-controller: bbr
sni: example.com
alpn: [h3]
heartbeat-interval: 10000
reduce-rtt: trueOutbound — TUIC v4 (legacy):
proxies:
- name: tuic-v4
type: tuic
server: example.com
port: 443
token: <token>
udp-relay-mode: native
sni: example.com
alpn: [h3]Inbound under listeners:
listeners:
- name: tuic-in
type: tuic
listen: 0.0.0.0
port: 443
users:
a3482e88-686a-4a58-8126-99c9df64b7bf: <alice password>
6f4e6c2c-8b1d-4b4f-8e2e-1f2b5c9e0a3b: <bob password>
certificate: /etc/mihomo/server.crt
private-key: /etc/mihomo/server.key
congestion-controller: bbr
alpn: [h3]Top-level tuic-server (legacy shape):
tuic-server:
enable: true
listen: 0.0.0.0:443
users:
a3482e88-686a-4a58-8126-99c9df64b7bf: <password>
certificate: /etc/mihomo/server.crt
private-key: /etc/mihomo/server.key
congestion-controller: bbr
alpn: [h3]Notes
- mihomo's TUIC outbound exposes timeouts and intervals in milliseconds, not seconds —
heartbeat-interval: 10000is ten seconds. Bothrequest-timeoutand the inbound'sauthentication-timeoutfollow the same convention. - The five congestion controllers are wired in
transport/tuic/common/congestion.go:20-53.bbris the modern v2 implementation;bbr_meta_v1/bbr_meta_v2exist purely for cross-compat with older clients. cwnd: 0is treated as32packets at runtime (transport/tuic/common/congestion.go:17-19).- The top-level
tuic-serverblock can only declare one TUIC inbound. For multiple TUIC inbounds on different ports, use thelisteners:form.
Cross-core notes
- Xray-core does not support TUIC. See TUIC — Xray-core.
- sing-box supports only TUIC v5 (no token form), uses snake_case field names (
congestion_control,udp_relay_mode), accepts Go-style duration strings ("3s") for timeouts, and has a smaller set of congestion-control choices (cubic,new_reno,bbr). See TUIC — sing-box.
Source: adapter/outbound/tuic.go:34-69 · v1.19.27 (5184081)
