Why policy groups are the missing layer after rule routing

Most tutorials stop once you can send video domains to one outbound and work SaaS to another. That is a huge milestone, but it still leaves a daily annoyance: inside a broad bucket labeled PROXY, which concrete server should win? If you use a select group, the answer is always you—by hand, every time a node congests or your provider reshuffles load. Clash and the Mihomo (Clash Meta) core add two automation primitives that work together: url-test to rank candidates by measured latency, and fallback to walk an ordered list until something answers the health check. When you place those under a single policy group that your rules section already references, you stop treating "PROXY" as a static dial and start treating it as a small self-healing control plane. If you are still choosing a client, start from our Clash download page so the YAML features below match the engine your GUI actually ships.

Think in three layers. First, rules decide which bucket of traffic is eligible for a proxy at all. Second, proxy-groups decide which real outbound the bucket uses. Third, proxies are the individual servers your subscription defined. This article is entirely about the second layer, and it assumes the first is already good enough that you are only unhappy about manual group switching. For background on splitting domestic versus overseas paths at the rule level, see our rule routing and domestic split guide—it complements what follows without repeating the same DNS examples.

What url-test, fallback, and select actually do

All three are entries under proxy-groups in your profile, and each name you declare there becomes a first-class target you can list inside other groups or your rules, exactly like a regular proxy. That composability is the secret to primary-backup: you are not limited to a flat list of server names.

select is manual. The core stores one active child from the proxies list and uses it until you or the API changes the selection. No automatic measurement happens unless your GUI shows delay badges through separate calls.

url-test is an automatic minimum-latency chooser. On each interval, the engine measures HTTP latency from each child to the url you specified (a tiny endpoint that should return 204 is typical). The winner is the lowest delay subject to tolerance: if a slightly slower node is still within tolerance of the best score, the core may keep the current pick to avoid rapid oscillation. That design targets "which exit feels fastest right now" for roughly symmetric nodes.

fallback is an automatic first-alive chooser. The engine still performs periodic checks when you supply url, but the selection semantics favor order: walk the proxies list from top to bottom and use the first member that currently passes the health test. In practice, people stack a premium or datacenter node first, then a cheaper spare, then DIRECT if you explicitly want a last-resort exit when everything else is dark. It is the policy-group version of a hardware failover port.

The Mihomo project adds other strategists—load-balance and smart selectors—but url-test and fallback cover the two intents behind "auto switching" in everyday language: optimize versus fail over. Mixing the two in one stack is not only valid; it is the standard recipe for a stable PROXY group.

url-test: YAML you can paste and adapt

Below is a minimal url-test group that only references concrete proxies. Replace the child names with entries that already exist in your top-level proxies list.

proxy-groups:
  - name: "AUTO-FAST"
    type: url-test
    proxies:
      - "Tokyo-A"
      - "Tokyo-B"
      - "Seoul-1"
    url: "http://www.gstatic.com/generate_204"
    interval: 300
    tolerance: 50
    lazy: true

interval is measured in seconds. Three hundred is a calm default: frequent enough to catch meaningful shifts, not so chatty that you hammer the test URL. tolerance is expressed in milliseconds relative to the best measured peer; raising it makes the selector stickier. lazy: true skips background tests until a flow actually needs the group, which helps on laptops you wake sporadically. Advanced builds sometimes add expected-status: 204 (or a regex) if the test endpoint is not the usual generate_204 page—always match what your provider expects so failures are real failures, not a parsing surprise.

fallback: build primary-backup without scripts

Primary-backup is literally the order of strings under proxies. The first name is primary; anything after is secondary. A compact pattern looks like this:

proxy-groups:
  - name: "FAILOVER-PRO"
    type: fallback
    proxies:
      - "Primary-SS"
      - "Backup-SS"
    url: "http://www.gstatic.com/generate_204"
    interval: 300

If the health check to url fails for Primary-SS, the group advances to Backup-SS on the next evaluation cycle. You can add more rows for tertiary exits or append DIRECT when you would rather hit the open Internet than a dead tunnel—just remember that the meaning of DIRECT at the end of a fallback chain is "give up on these proxies for now," not "route according to a different table." Keep domestic exceptions in the rules section where they are easier to audit.

Nesting: url-test inside fallback for real-world stacks

Single-region consumers often have two different problems at once: several acceptable nodes in the same city, and one extra provider they trust only as backup. The idiomatic fix is a url-test for the first hop and a fallback that references that group as a single child.

proxy-groups:
  - name: "JP-POOL"
    type: url-test
    proxies:
      - "JP-1"
      - "JP-2"
    url: "http://www.gstatic.com/generate_204"
    interval: 300
    tolerance: 30

  - name: "PROXY"
    type: fallback
    proxies:
      - "JP-POOL"
      - "US-STANDBY"
    url: "http://www.gstatic.com/generate_204"
    interval: 300

Here, PROXY is the only name you need in your rules section for generic foreign traffic. Internally, JP-POOL always tracks the best Japanese exit, and if that whole pool becomes unhealthy from the point of view of the outer fallback check, the stack jumps to US-STANDBY. The mental model is two knobs: the inner group optimizes, the outer group survives.

Wiring PROXY: keep rules simple and let groups do the work

After you add nested groups, audit every place your YAML references a policy name. A common foot-gun is duplicating: one rule that still points to an old select while another points to the new PROXY fallback, producing inconsistent behavior between browsers and background apps. A cleanup pass in your editor is faster than a week of "why did only Slack switch?" tickets to yourself. Also check whether your subscription merge logic or remote profile overwrites proxy-groups on each update—if it does, move stable definitions into a local include file the merge preserves.

GUI clients like Clash Verge Rev and FlClash show the final tree; use that view to ensure PROXY is the one visible in the quick selector if you still use manual mode for other profiles. The goal is a single, memorable anchor that always reflects automation underneath.

Tested verification steps: prove auto-switching actually happened

Automation that you cannot see might as well be superstition. After reloading the profile, run through a short checklist before you trust a long-haul download or a work call. First, open the client dashboard or the built-in Groups view and look for a Delay column beside each child proxy. The numbers should move every time an interval elapses, which confirms tests are not blocked by a corporate firewall that intercepts the test URL. Second, start a log view at info level and look for group evaluation lines when you expect them; you should not need debug noise unless you are investigating a false negative.

Third, simulate failure in a safe way. Pause or block the primary outbound in your provider console if you can, or temporarily rename it in YAML so the health check must fail, then watch whether the outer fallback advances within one or two interval cycles. Fourth, browse to a well-known IP echo service through the tunnel and write down the public address while on primary, then after failover confirm the address changes if your backup is truly a different region. This step catches silent misconfigurations where both children route through the same supernode because of bad relay metadata.

Optional: the external controller snapshot

Power users can query the /proxies REST surface when external-controller is enabled. A GET response lists each group with its now selection and, for many builds, the last measured history. That interface is the fastest way to script alerts if you run headless Linux with systemd—pair it with the patterns in our headless Clash Meta deployment guide so a silent failover does not go unnoticed in journald.

Tuning: intervals, test URLs, and when not to over-automate

Shorter interval values make the system feel responsive but multiply background HTTP probes. On metered or hostile networks, a sub-minute cadence is usually unnecessary. Symmetrically, intervals longer than ten minutes can leave you riding a congested path long after a better one appeared. If you are sensitive to flapping, raise tolerance first before you slash the interval, because raw delay noise often comes from Wi-Fi airtime, not the exit.

Test URL choice matters. Public204-style endpoints are popular because they are small and usually permissive, yet they do not always reflect the latency to the SaaS you really use. If your pain is a single service, and your provider allows it, consider an HTTPS GET that mirrors the first byte of a realistic API. Keep requests cache-friendly and respect rate limits. Never point tests at subscription refresh URLs or the controller API, or you will conflate health with unrelated auth failures.

Finally, not every workload benefits from url-test. Long-lived WebSockets and voice sessions care more about stability than a fifty-millisecond win on port 443. A sticky manual selection or a conservative fallback with wide intervals sometimes feels better in real life because it avoids mid-call jumps. url-test is best when the user experience is "web pages and downloads" where a different exit mid-session is acceptable.

Troubleshooting: symptoms mapped to quick fixes

The group never leaves the first proxy

Check that the child names match exactly, including case, and that both members are not the same underlying hop because of a duplicate tag in the subscription. If health checks are disabled because url is missing, fallback can appear stuck in a way that is actually undefined—always supply a test URL in modern Mihomo.

url-test bounces every few seconds

Increase tolerance, lengthen interval, or remove one nearly identical node that differs only in label. Two peers with the same real path often produce statistically tied delays that oscillate the selector.

Fallback jumps to DIRECT unexpectedly

Confirm that the nodes above DIRECT actually fail the url test rather than returning an unexpected status code, and that your system clock is correct—TLS failures from skewed time can look like downed servers. A subscription or TLS time guide may help: see subscription update and time sync fixes.

One-page comparison: pick the right type

Use this as a design checklist, not a dogma. Hybrid stacks are common.

Group type User intent When it shines
select Manual Debugging, A/B on one node, or sessions that hate mid-flight changes.
url-test Lowest delay among peers Region pools, multi-node subscriptions, and browsing workloads.
fallback Ordered survival Premium first, budget spare, or tunnel then DIRECT for resilience.

Deeper reading and a calm closing note

Policy groups do not replace thoughtful rules; they complete them. Once the routing table sends generic HTTPS to PROXY, a well-structured url-test and fallback stack is what keeps you from living inside the group picker. Document the names you choose—future you, editing YAML at midnight, will thank present you. For broader setup topics, our documentation hub links additional tutorials and examples that align with the Mihomo feature set you run today.

Compared with micromanaging a dozen single proxies, a tiered Clash policy group with url-test and fallback is usually calmer: fewer impulsive hand switches, fewer "which tab used which node" puzzles, and logs that read like a story instead of a mystery. When you are ready to standardize on a build that actually exposes every knob in this article, download Clash for free and experience the difference from our official page.