1. What relay is (and is not)
In Clash-family cores, a proxy-groups entry with type: relay lists two or more existing proxy names. The core connects them in order: the first name is the hop closest to you, the last name is the hop closest to the destination. That is different from a selector, where you pick one alternative, and different from a url-test group that measures a pool. Relay is a fixed multi-hop path, which is why operations teams also call it a proxy chain in the generic sense, even though the configuration keyword is relay.
Relay is not a substitute for a full site matrix: if you need automatic failover or latency-based choice across many exits, you still use url-test, fallback, or load-balance in separate groups, and you may point those groups at a relay group as a single selectable policy. The companion article on url-test interval and tolerance in Mihomo is the right next read once your chain itself dials correctly.
Relay is also not a magic “double encryption always” switch. If both hops are plain HTTP or SOCKS to untrusted middle boxes, you still inherit their trust and failure domains. The value is path control: a stable entry in one jurisdiction and a business exit in another, or a domestic bridge before a high-latency overseas peer.
If the rest of this page feels abstract, start from the configuration overview on this site so the proxies and proxy-groups keys line up with how your GUI merges subscriptions and user rules.
2. Minimal working YAML: proxies, then relay
Every name you place under a relay’s proxies array must already exist. Those names usually come from the proxies list, from proxy-providers after a successful fetch, or from another proxy-groups name that the core can resolve. A frequent beginner mistake is to paste a relay block before the subscription has populated the leaf nodes, which produces “proxy not found” at runtime—same family of failure we cover for empty lists in the empty node list and provider article, except the log mentions your chain name.
Below is a structural example. Replace server fields with values from your provider, and confirm the keys against the exact core build you run—field names for TLS client settings and obfuscation vary slightly between legacy Clash and modern Mihomo lines.
proxies:
- name: BRIDGE-TOK
type: ss
server: 198.51.100.10
port: 8388
cipher: aes-256-gcm
password: "********"
- name: EXIT-NYC
type: vmess
server: 203.0.113.20
port: 443
uuid: 00000000-0000-0000-0000-000000000000
alterId: 0
tls: true
servername: example.com
proxy-groups:
- name: CHAIN-TOK-NYC
type: relay
proxies:
- BRIDGE-TOK
- EXIT-NYC
- name: PROXY
type: select
proxies:
- CHAIN-TOK-NYC
- DIRECT
Notice the separation: the relay group only references other proxies. It does not embed server hostnames itself. The PROXY selector (or your global root selector, depending on profile layout) is what the rules section usually targets, so a rule that sends traffic to PROXY can now use the proxy chain as one of the choices.
3. Hop order and which IP the world sees
Order in the proxies array under a relay is not cosmetic. The first hop is where your Clash process opens the first TCP or UDP socket outward; the last hop is where the last TLS or SOCKS handshake toward the origin usually happens, unless a protocol in the middle terminates early. For HTTPS sites, the exit hop often determines visible source ASN and any geo that the site measures after TCP establishment.
When you debug “why does this service still see country X?”, start by identifying which hop is supposed to be the exit, then test that hop alone through a single-hop selector. If the exit alone is wrong, the relay is faithfully chaining the wrong last mile; if the exit alone is right but the chain is not, the first hop may be re-writing traffic or failing a protocol the second hop needs.
Some users try to add three or more names for exotic paths. Cores do allow it, but each extra hop multiplies latency and failure points. For anything beyond two hops, keep a text diagram of the chain in your notes so you do not accidentally create a circle when you rename groups later.
4. Wiring relay into policy groups and rules
Once CHAIN-… exists, treat it as a first-class policy the same as any other group name. A common pattern is to nest: select for manual choice, or url-test to pick the faster of several candidate chains. Keep those measurement groups on generous tolerance if you also read the url-test guide—bouncing between chains is worse than bouncing between single nodes, because the inner relay rebuilds more state.
In the rules section, you will usually continue to match DOMAIN / IP-CIDR / GEOIP to a top-level group such as PROXY, PROXY-MEDIA, or PROXY-CHAIN-ONLY. A practical split is to reserve one selector that contains only CHAIN-… entries and point sensitive domains to that group, while the rest of the traffic uses a simpler one-hop pool. That reduces confusion when a random global rule was never meant to ride a delicate double path.
If you mix DIRECT and a relay in the same selector, label the UI strings clearly. Many GUI clients show only the name string; “CHAIN-DEBUG” reads better on a phone at midnight than “G-02.”
5. “Proxy not found,” loops, and invalid nesting
The fastest compile-time style error is a typo in a hop name. The relay line references EXIT-NY but the proxies list still says EXIT-NYC. Cores and linters will usually surface that early. Slightly harder is a name that exists only in a provider that has not been downloaded yet—your inline YAML is valid, but the runtime has zero leaf rows. Refresh providers before blaming the relay keyword.
Circular dependencies are the second class: relay group A includes group B, while B includes A, or a chain accidentally references itself through a chain of select indirection. Modern builds detect many of those, but a messy profile can still make the control plane throw errors when a rule points at the cycle. The fix is to flatten: leaf proxies only inside relay, and keep select groups above the relay as pure umbrellas.
Another subtle issue is using a relay where the core expected a single out proxy for a feature—some subsystems in niche builds restrict hop types. When in doubt, test with two simple ss or vmess nodes you control before layering obfuscation. If the minimal pair works, the problem was protocol or TLS interaction, not proxy chains in principle.
6. Dial errors, TLS, and the middle hop’s obligations
When a hop fails, logs often read like ordinary upstream failures: dial tcp to an IP, i/o timeout, or connection refused. The only difference is the stack frame mentions which hop was active. Always ask: did the first hop fail before the second had a chance? Temporarily set your rule to use only the first hop; if that alone cannot reach the second hop’s address and port, the chain will never work until that allow path exists—think firewall on the middle server, security group, or a domestic peer that cannot exit to a foreign port.
TLS and SNI issues compound on the last hop. If a site breaks only through the chain, compare a capture of the same site through the final hop as a single node. The article on TLS handshake timeout and SNI explains how server name, ALPN, and node-side filtering interact. A relay does not remove those requirements; it adds another place where a middle hop might throttle long-lived HTTPS streams.
DNS resolution timing matters, especially if the first hop and the second hop disagree about which A record to use. Fake-ip and TUN stacks that resolve locally before handing an IP to the outbound can mask hostnames. If a symptom looks DNS-shaped, revisit fake-ip and DNS before reworking the chain.
7. UDP, QUIC, and when a chain is the wrong tool
Not every VMess or Shadowsocks build forwards UDP the way you expect across multiple hops. Voice chat, QUIC on UDP/443, and some games can fail with perfectly fine TCP through the same relay path. The failure mode is often “works in the browser, fails in the game,” because the game pins UDP. When that happens, try the same exit as a single hop, then add the first hop back. If UDP breaks only in chain mode, you may be hitting a core limitation for that pair of protocols, not a user mistake.
Some users chain specifically to “hide the exit from the entry.” Remember that the first hop still sees your metadata volume and timing; a relay is not a complete anonymity system. It is a routing convenience inside Clash and Mihomo semantics.
8. How Mihomo-style logs name chains and hops
On recent Mihomo builds, the log may include the policy name and the leaf proxy selected under it, which is invaluable when a url-test has rotated to a new chain. Enable debug level briefly when you need to see whether a rule hit PROXY or a more specific group, but turn the level back down afterwards—permanent debug on busy machines fills disks.
When a user says “I see a dial error in the chain,” the first triage is to map the line to: (a) which hop, (b) which destination IP, (c) whether the failure is TCP connect or TLS, and (d) whether the same destination works in DIRECT to exclude local ISP block. The matrix is boring but it saves hours of swapping subscriptions.
If you are automating with the API, the same names you put in YAML appear in JSON payloads. Treat a relay as an opaque proxy from the outside: clients ask for a connection through policy CHAIN-…, and the core unwinds it to ordered hops. That is why a duplicate name between proxies and proxy-groups is never allowed in well-formed files.
9. Symptom-to-action table
| Log or UI signal | Likely cause | First fix |
|---|---|---|
| “proxy not found” for the relay | Typo, missing provider fetch, or wrong merge order | Re-read merged YAML; refresh providers; compare names character for character |
First hop dial OK, second hop timeout |
Bridge cannot reach exit host:port, or wrong IP family | Test exit alone; check firewall between hops; try IPv4-only |
| Site loads over single exit, broken only via relay | Middle hop strips or throttles long TLS; DNS split between hops | Match SNI; simplify protocols; read DNS + fake-ip guides |
| UDP app fails; browser fine | UDP not carried through that chain or blocked on first hop | Test UDP on single hop; try different node types; consider TCP-only path |
10. Summary
A type: relay proxy group is the native Clash / Mihomo way to express multi-hop paths in YAML: list real upstream names in order, then reference the relay from a selector or a rule target like any other policy. Most painful incidents are not mystery bugs—they are wrong names, half-fetched providers, a middle hop that cannot reach the final hop, or TLS / DNS details that a single hop hid until you stacked two. Work the chain one hop at a time, keep url-test groups stable when you nest them under chains, and use the symptom table as a triage order rather than re-importing a subscription.
Good tooling makes that workflow sustainable: a maintained GUI that shows effective config, a log view that names policies, and a download channel that matches your core version. For builds aligned with the features discussed here, use the official download page on this site so the app and engine track together.