Rules “not working” usually means an unexpected match

Clash evaluates the rules: list from top to bottom and stops at the first match. There is no partial credit. If a broad GEOIP or IP-CIDR line sits above your boutique streaming block, that broad line wins forever for the metadata the kernel saw—often an IP address after resolution, not the pretty hostname you typed in a browser. Conversely, if nothing matches until the end, the engine falls through to FINAL, which is simply the last policy catch-all in most profiles. Users blame FINAL as if it were a bug; it is closer to a confession: “nothing earlier claimed this flow.”

The other half of confusion is policy groups. A rule rarely names a bare vmess or trojan outbound directly in large configs. It names PROXY, Streaming, Auto, or another alias. Those groups may be SELECT (you pick), URL-TEST (the core picks by latency), or FALLBACK (ordered probe). Nested groups are common: PROXY contains Auto contains three airport nodes. The log records the matched rule → target group; your GUI highlights a leaf node. When those disagree, the fix is to read the chain, not to sprinkle duplicate domain lines at random.

This article assumes Meta/mihomo-class cores with modern logging. If you need broader rule-writing patterns—Rule Providers, merge hygiene, naming—start from the YAML routing and policy group guide first, then return here when a specific hostname misbehaves.

Step 1: Freeze variables—one app, one URL, one profile

Troubleshooting while twelve browser tabs auto-refresh is how you collect contradictory logs. Pick one reproducer: for example “Safari private window opening https://www.example.com with only Clash enabled and no second VPN.” Note your mode—system proxy, mixed port only, or TUN—because capture layers change which process speaks to the core. If you recently merged a remote subscription URL with local overrides, export the effective running config from the client (many GUIs expose this) so you are not editing a stale file while the engine runs another.

Write down three expectations before you open logs: which policy group should own the flow, which country or tag that group should use today, and whether you insist on DIRECT. Those expectations turn log reading into a boolean test instead of vibes. If you skip this step, you will chase moving targets when the real issue is a rotating URL-TEST winner or a manual mode flip in the tray menu.

Close parallel tools Browser VPN extensions, corporate agents, and second hooks can steal DNS or TLS before Clash labels the flow. Suspend them during the six steps so “matched rule” refers to one owner.

Step 2: Enable debug logging for routing decisions

Informational logs tell you that a tunnel came up; they rarely print which YAML line justified it. Switch the core—or the relevant module—to debug verbosity for a short capture window. Desktop GUIs often expose this under advanced settings; headless setups use log-level in profile or flags. Accept that debug mode is noisy: limit the session to tens of seconds, reproduce once cleanly, then step down to info.

What you are hunting is text that ties a connection identifier—port tuple, process name, sniffed host, or destination—to a rule payload. Different builds phrase it differently; learn your build’s phrasing once and reuse. Screenshots of redacted lines are worth archiving in your notes because the same wording reappears when you teach someone else on your LAN. If your client only shows a trimmed UI log, open the on-disk log path the vendor documents.

Security reminder: debug logs may include domains you visited. Rotate files, avoid posting them publicly without scrubbing, and remember that a subscription URL itself is credentials-adjacent—never paste it beside detailed routing traces in tickets.

Step 3: Reproduce and read the matched rule line literally

Trigger the connection once. Find the log record that states the engine chose a policy because a specific rule matched. Read that line as law, not as a suggestion. It tells you the rule type (domain suffix, keyword, GEOIP, process name, inline rule-set reference, etc.), the payload, and the policy target string—often a group name, sometimes DIRECT or REJECT.

If the matched type surprises you—say GEOIP when you expected DOMAIN-SUFFIX—pause. That usually signals metadata mismatch: the connection arrived with an IP the sniff layer never mapped back to the hostname you care about, or your browser resolved the name outside Clash’s DNS path. This is where Fake-IP versus Redir-Host documentation saves hours. It is also why TLS sniff toggles matter: without host information, rules fall back to address-based tests that reorder your intended precedence.

When the matched rule is exactly the line you wrote yet the “wrong” node still carries traffic, proceed to Steps 4–5. The rule did its job; the policy group order or selection did not.

Advanced operators sometimes keep a scratchpad template for notes: timestamp, destination tuple, process name if logged, sniffed Server Name when TLS is in play, the verbatim rule string, and the resolved outbound after walking proxies. That discipline matters when a service uses dozens of CDNs across a session—your first connection may hit a front door domain while the video segment resolves to a cache you never listed. If each hop logs a consistent policy target but the GUI still shows an unexpected region, you are often looking at a subscription URL node whose tag is stale, not a missing rule. Refresh the provider, verify the node label matches the geography you purchased, and only then return to rule order debates.

Step 4: Trace from policy target to leaf outbound

Open proxy-groups: in the running config. Locate the group named by the matched rule. If it is a SELECT group, read its proxies: list in order: proxies, other groups, DIRECT, REJECT. The UI selection you think you set may have been overwritten by a one-click “GLOBAL” change, a profile reload, or a remote merge that reintroduced an old default.

For URL-TEST and FALLBACK, note the active candidate. Latency tests flip winners when regions jitter; a “wrong country” may simply be the healthiest node under your test URL. If that is unacceptable, narrow the candidate list, pin a static SELECT for that service, or split services so streaming never shares an Auto pool with generic browsing.

Nested groups demand recursion. A rule sends you to PROXY; PROXY is a SELECT whose current member is Auto-US; Auto-US is URL-TEST across three nodes. The log’s matched rule will not narrate that chain—you infer it by walking the YAML. This walk is the skill that separates operators who “fixed it by moving lines” from those who know why the move worked.

When two templates disagree—one author names 🎯默认 while another names PROXY—a merge can leave both groups alive with overlapping members. The rule that still points at the obsolete alias becomes dead weight you forget exists until a stray FINAL alias routes traffic through it. Periodic inventory of proxy-groups names referenced by rules: versus names actually defined catches that class of typo early. Likewise, if you renamed a group in the GUI without editing YAML, some clients rewrite files; others keep ghosts until you export again. Export, diff, reconcile.

Step 5: Understand FINAL, merge order, and duplicate coverage

FINAL is not a magic bypass. It is the default basin for flows that survived every earlier rule. Seeing FINAL for a domain you “covered” means one of the following: your coverage never executed (different profile), executed too late (order), used the wrong matcher type for the metadata you had, or was commented out by a merge bug (duplicate keys in YAML, with the later key silently winning). Subscription generators sometimes ship massive rule providers; your personal DOMAIN line may be physically present but positioned after a provider already caught the same traffic with another policy.

When you maintain both local snippets and remote bundles, diff the concatenated rule array mentally: remote first or local first depends on how your client stitches files. A common remedy is moving needle rules above provider imports or splitting providers so domestic blocks cannot shadow boutique exceptions. The exact mechanism is client-specific; the invariant is that the first win is the only win.

If FINAL routes somewhere you dislike, change FINAL’s target group deliberately—but also ask whether you want a broad catch-all at all. Many templates set FINAL to PROXY for novice safety; enterprise splits often set FINAL to DIRECT with explicit exceptions above. Neither is “correct” universally; both are observable in logs once you trigger unmatched traffic on purpose.

Step 6: Confirm capture mode, TUN, and split-brain DNS

Sometimes the matched rule is sane and the group chain is sane—yet an application still exits elsewhere because it never transited the core you configured. Classic examples: tools with hard-coded proxies, containers using a bridge DNS, or mobile emulators with isolated networking. TUN mode exists partly to close those gaps by lifting traffic to the tunnel interface; if you insist on classic system proxy-only mode, accept that some binaries will skirt it.

Use the same six-step ladder when switching TUN on or off: re-capture logs, confirm the sniffed hostname appears, and verify whether GEOIP sees the destination you think it sees. IPv6 paths can duplicate the confusion described in our IPv6 split-routing piece—dual stack doubles the opportunities for one family to obey a different rule than the other.

End with a regression checklist: revert log level, document the one line you moved or the group you renamed, and—if you share configs—note why so your future self does not “fix” it back during the next midnight import from a refreshed subscription URL.

Symptom quick map

What you observe What to inspect first
Logs show FINAL for a domain you wrote explicitly Earlier broad match; merge order; DOMAIN vs IP path after DNS
Matched DOMAIN rule but exit country wrong Nested URL-TEST, manual SELECT drift, rotating winners
Browser differs from curl for the same URL Secure DNS, captive extensions, per-app proxy bypass
Rule works briefly after reload then regresses Remote provider update; duplicate YAML keys; client overwriting local edits
Only QUIC-heavy sites misroute UDP handling, sniff coverage, TUN inclusiveness

Open source and upstream references

Syntax and log labels evolve between Meta releases. When in doubt, consult the mihomo repository for authoritative behavior and issue history. GitHub is ideal for transparency around the engine—not as the primary installer channel. For everyday binaries, prefer the project’s release pages and the on-site download flow linked below so your bundle matches what the GUI expects.

Frequently asked questions

Should I delete FINAL? No. Replace or retarget it. Profiles need a terminal decision; removing the line breaks validity or leaves the client’s implicit default—neither is easier to debug.

Are “matched rule” lines identical across Clash flavors? Wording varies. Focus on the triple: matcher type, payload, policy target. Once you map your build’s phrasing, the mental model here stays valid.

Do rule providers make logging harder? They make sources harder to read in isolation, but the running config still expands to concrete rules. If logs cite a provider’s internal name, open that provider file or inspect the merged expansion your client offers.

Closing thoughts

Split routing debug is satisfying because the engine is deterministic: first match wins, groups resolve predictably, and FINAL tells you when nothing else did. Pain comes from hidden merges, moving DNS paths, and nested selection—not from mythical ghosts in YAML. Pair this workflow with solid policy group order habits and occasional log level bursts, and “wrong node” tickets shrink to minutes instead of weekends.

Transparent tools reward patient operators. Compared with closed clients that hide decisions, Clash Meta’s logs turn rule match disputes into factual readings—which is why teams still standardize on it in 2026 despite noisier markets.

Download Clash for free and capture one failing flow with debug logs: confirm the matched rule, walk the policy group order to the leaf node, and only then edit YAML—your subscription URL import stays stable while you reason about FINAL with evidence, not guesswork.

For structure and Rule Provider hygiene, continue with the YAML routing guide; browse the full tech column for scenario-specific domain lists.