Custom Clash Rules: Route Specific Apps to Specific Proxy Nodes

Subscription profiles ship thousands of lines, yet they rarely know your habits: which game should ride a low-latency Tokyo node, which browser should exit in another country, and which downloader should stay on DIRECT. This guide explains how custom rules sit on top of proxy-groups, how rule order actually works, and how to use process-based matching—where your core supports it—to steer individual applications without turning your YAML into chaos.

What “custom rules” really means in Clash

A Clash configuration is not one monolithic dial labeled “VPN on.” It is a small program: proxies list real servers, proxy-groups schedule them, and rules decide which group (or DIRECT / REJECT) each connection should use. “Custom rules” simply means lines you add or prepend so that your intentions override the generic GEOIP or MATCH behavior that remote providers ship by default.

Most users first touch custom rules when something feels wrong: a streaming domain lands on the wrong region, a corporate SaaS breaks through an unexpected exit IP, or a game launcher updates slowly because it is pinned to a congested automatic pool. Fixing those cases is less about memorizing every keyword and more about respecting two invariants: rules are evaluated from top to bottom, and the first match wins. Everything else—domain sets, process names, IP lists—is syntax wrapped around that model.

If you have not yet structured proxy-groups, read our proxy-groups guide first. Rules point at group names; groups define which physical node you eventually get. A perfect rule that targets a missing group name still fails at runtime, so keep names synchronized across sections.

Rule order: the detail that explains “why Clash ignored my line”

Imagine your profile ends with a broad line such as MATCH,Auto. Any connection that reaches that line uses whatever Auto resolves to—often a url-test pool. If you append a more specific domain rule after MATCH, it will never execute, because every flow already matched earlier. The fix is not “more specific syntax”; it is moving the specific rule above the catch-all.

Community templates sometimes split rules into sections with comments: domestic direct lists, streaming blocks, advertising filters, then a final MATCH. When you merge upstream updates, preserve that ordering discipline. A practical habit is to keep a clearly marked block near the top—user overrides—so you can reapply it after importing a refreshed subscription without diffing ten thousand lines by hand.

Another subtlety: some rule types inspect different layers of a connection. Domain-based rules need a hostname; if an application talks by raw IP and you have no Sniff/DNS data yet, the core may skip straight to IP-based rules. That is not “broken rules”; it is a reminder that routing and DNS are coupled. When debugging, watch your client logs to see whether the matcher saw a domain or only an address.

Essential rule types for everyday split routing

Clash-family cores share a vocabulary that looks intimidating on the first read but maps cleanly to user intent. You do not need every keyword on day one—master the handful below and you will cover most home lab scenarios.

DOMAIN matches a single fully qualified hostname. Use it for one-off services you care about, such as a bank portal or a vendor API that is sensitive to egress geography. DOMAIN-SUFFIX matches a suffix, so example.com also covers cdn.example.com—handy for platforms that shard across many subdomains.

DOMAIN-KEYWORD performs substring matching. It is convenient but coarse: a short keyword can collide with unrelated hosts, so prefer suffix rules when you can. IP-CIDR and GEOIP operate on destination addresses—essential for apps that hard-code IPs or for split tunneling by country without enumerating domains.

Each line ends with a policy: a proxy-groups name, DIRECT, REJECT, or in advanced setups another built-in action your core documents. The policy string must match an existing group name character for character, including spaces if you insist on poetic labels.

# Examples (names must exist in proxy-groups)
DOMAIN,www.example.org,Streaming-US
DOMAIN-SUFFIX,steamcontent.com,DIRECT
DOMAIN-KEYWORD,google,Auto
IP-CIDR,10.0.0.0/8,DIRECT
GEOIP,CN,DIRECT
💡 Tip Put IP-CIDR entries for LAN and VPN interface ranges on DIRECT before rules that send generic traffic to a proxy, so local printers and file shares do not detour through Tokyo.

Scaling with RULE-SET instead of megabyte-long YAML

Maintaining giant inline lists by hand does not scale. Rule sets let you reference externally hosted collections—often domain lists for a category like “streaming” or “malware”—and merge them into the evaluator without pasting fifty thousand lines into your file. In Clash Meta (Mihomo) configurations you typically declare providers under rule-providers and then reference them with RULE-SET lines in rules.

The benefit is operational: you can update or swap a provider independently of your subscription blob, throttle how often the client refreshes it, and keep your personal overrides in a short, readable section. The trade-off is trust and transparency: you are executing policy authored elsewhere, so prefer sources with clear maintenance stories and checksum habits if you mirror files locally.

When combining multiple providers, remember the first-match principle still applies across the expanded list. If two sets overlap, whichever rule appears earlier in your rules array wins—there is no automatic merge conflict resolution inside the core.

A typical Meta-style skeleton introduces a provider with a remote URL, a behavior tag such as classical or domain depending on the file format, and an interval for refresh. The precise keys differ slightly between releases, so treat the snippet below as illustrative—copy the structure from your core’s sample config rather than assuming field names never change.

rule-providers:
  media-us:
    type: http
    behavior: classical
    url: "https://example.com/rules/media-us.yaml"
    path: ./ruleset/media-us.yaml
    interval: 86400

rules:
  - RULE-SET,media-us,Streaming-US
  # ... your hand-written lines above MATCH ...

Notice how the rules section stays legible: one RULE-SET line can expand to hundreds of underlying rows at load time. That legibility matters when you collaborate with others or return to a profile months later. You can also host rule sets on your own server or check them into a private repository, then point the url field at a static file you control—useful when upstream lists move faster than you like or when you want to freeze a known-good snapshot before a risky experiment.

Offline scenarios deserve a mention. If you air-gap a machine or work on flaky hotel Wi-Fi, cached path files let Clash start with the last downloaded set instead of failing closed. Pair that with sane fallbacks in proxy-groups so a missing rule provider does not strand you without connectivity—another reason DIRECT and fallback groups belong in every serious template.

Per-application routing with PROCESS-NAME

Domains are perfect for browsers and most networked services, but games, IDEs, and thick clients sometimes open dozens of connections to CDNs with opaque hostnames. For those cases, process-based rules match the local executable responsible for the socket. In Mihomo-class cores this is commonly exposed as PROCESS-NAME (exact names vary slightly by fork—always confirm against your core’s documentation).

On Windows, you usually match the binary file name, such as chrome.exe or GameClient.exe. On macOS, you typically use the process name as reported by the system—often similar to the executable inside the app bundle, not the friendly “App Store” title. Linux desktop builds may expose comparable matchers; headless servers rarely need this because services are fewer and easier to tag by port or user.

Think of process rules as a last-resort scalpel when hostname-based policy is too brittle. A browser might share one binary across work tabs and personal tabs; routing by chrome.exe therefore affects the entire program, not individual tabs. Some teams solve that by using separate browser profiles with different executable copies, or by relying on domain rules for web traffic and reserving process rules for non-HTTP stacks. Neither approach is “wrong”—they reflect different comfort levels with complexity.

Privileges matter too. On modern operating systems, elevated processes sometimes bypass user-level hooks; games that run as administrator may not behave identically to your YAML expectations until you align elevation and capture mode. If you see inconsistent hits in logs, compare the security context of the process against the context of your Clash helper or TUN adapter.

Because process rules key off local OS metadata, they interact with how traffic is captured. If an application ignores the system HTTP proxy and you are not running TUN, the connection might never hit Clash at all—so there is nothing to classify. Our TUN mode deep dive explains when transparent capture is required so stubborn binaries still flow through your policy stack.

# Hypothetical: send a Windows game through a dedicated low-latency group
PROCESS-NAME,SomeGame.exe,Game-JP

# Another binary stays domestic
PROCESS-NAME,HomeBanking.exe,DIRECT

Be mindful of updaters and launchers: many games start a small executable that then spawns the real binary. If your rule targets the wrong process, symptoms look like “sometimes it works.” Use OS tools—Task Manager, Activity Monitor, or ps—to verify the active process name during the misbehaving action.

Android, package names, and mobile GUIs

On Android, per-app routing historically lived partly in the VPN service API rather than pure YAML. Modern Clash Android derivatives may expose package-name matchers or equivalent UI toggles that compile into rules your core understands. The exact keyword and permission story depend on the GUI and Android version, so treat mobile examples in forum posts as hints, not portable truth.

What transfers from desktop practice is the design pattern: define a purpose-specific group (for example Media-US), wire a matcher to it, and keep testing on cellular and Wi-Fi separately—some carriers intercept DNS in ways that change which rules fire first.

DNS, Sniff, and why “the domain rule should match” but does not

Clash does not magically see hostnames for every TCP flow. If a client resolves DNS through a channel the core does not control, you might only observe an IP when the connection opens. Sniffing features attempt to recover domain information from TLS SNI or HTTP Host headers, but they are not omniscient. When you see unexpected routing, check whether fake-ip mode, redir-host, or a local DNS listener is in play—each shifts when and how names become visible.

A pragmatic workflow is: reproduce the issue with logging enabled, note whether the evaluator logged a domain or an IP, then adjust DNS settings or insert an IP-CIDR fallback for that netblock if you must. Blindly stacking more DOMAIN-SUFFIX lines rarely fixes a visibility problem rooted in DNS.

Split-horizon DNS adds another wrinkle. Corporate VPNs and some ISPs intercept queries or return different answers depending on where you sit. Clash may be doing exactly what you asked while the answers you receive differ from a public resolver’s view. When debugging cross-environment issues, compare query results from inside the tunnel versus on plain DIRECT DNS—discrepancies there explain many “it works on my phone but not my laptop” threads.

IPv6 is easy to overlook. If your OS prefers AAAA records and your rules only cover IPv4 literals, you might route IPv4 through a proxy while IPv6 escapes on DIRECT, producing bizarre partial failures on dual-stack sites. Either align IPv6 policy explicitly or disable IPv6 at the OS level if that matches your threat model—just document the choice so future you understands why.

End-to-end recipe: build a “purpose group,” then pin rules to it

Start from a minimal skeleton. First, declare or reuse real nodes in proxies. Second, create a select or url-test group dedicated to one scenario—say Streaming-US containing only United States nodes appropriate for a given service. Third, insert domain or process rules that send the relevant traffic to Streaming-US above your generic MATCH line. Fourth, reload the profile and verify with a known test (a regional lookup page, the service’s own status tool, or a traceroute from inside the app if available).

This pattern mirrors how network engineers treat policy routing: isolate intent into named classes, attach traffic to classes, and keep a default route for everything else. Your future self will thank you when the subscription refreshes and reintroduces a bloated default rule list—you can re-paste a twenty-line override block instead of reconstructing tribal knowledge from memory.

For client ergonomics, expose only a handful of top-level selectors in the UI; nest the exotic pools beneath them so casual users are not overwhelmed. The proxy-groups guide includes nesting patterns that pair well with domain and process rules.

Consider a concrete narrative. Suppose you want US catalog access for a video service while keeping general browsing on an automatic fastest pool. You might create Streaming-US as a select with two US nodes you trust, add DOMAIN-SUFFIX lines for that provider’s known CDN hostnames, and leave MATCH,Auto untouched below. If the provider rotates subdomains aggressively, promote the stable suffix entries from community lists into your RULE-SET provider so you can refresh them without editing the main config.

Another scenario is split traffic for work: corporate Git and package registries on DIRECT, public GitHub through your normal proxy, and internal IP ranges excluded via IP-CIDR. Ordering matters: put internal ranges before any broad GEOIP rule that might otherwise grab the same packets. Document the intent in a one-line comment—YAML parsers ignore # notes, but humans remember them when onboarding new machines.

Testing, logging, and avoiding superstition

When a rule “does not work,” resist the urge to shuffle keywords randomly. Instead, confirm four facts in order: the rule is above broader matches; the policy name matches a live group; the connection actually traverses Clash (TUN or system proxy as needed); and DNS provides enough information for domain rules to engage. Most mysteries collapse once one of those is false.

Keep a scratch YAML in Git—even a private repo—so you can bisect changes. Snapshot before merging vendor updates. If you collaborate with housemates or teammates, document why a quirky process rule exists; future updates to game patches love to rename executables.

Structured tests beat anecdote. Build a checklist: open a known IP-only service, a known domain-only service, a split-DNS internal site, and a UDP-heavy application if your stack uses it. Note which proxy group each hit in the log. That matrix exposes misconfigurations faster than scrolling social feeds for “magic” toggles.

Performance-wise, enormous rule lists can add memory and CPU overhead on low-end routers. If you deploy Clash on embedded hardware, prefer compact RULE-SET sources and aggressive interval tuning over importing every community list “just in case.” Measure with real workloads—home labs rarely need the same aggressiveness as public Wi-Fi kiosks.

Common pitfalls we see in support threads

Catch-all too early. Users paste a clever DOMAIN line at the bottom of a file that already matched MATCH. Move it up.

Conflicting subscriptions. Two imports both define rules differently; the GUI merges them in an order you did not expect. Learn your client’s merge semantics or consolidate into one authoritative file for rules you care about.

Misidentified process names. Typos, wrong child processes, and case sensitivity on some platforms waste hours. Verify live names.

Chasing latency with rules alone. Rules choose a policy; url-test tuning chooses a node inside that policy. If jitter remains high, revisit group probes and server choice—not another fifty domain lines.

Copy-paste from outdated tutorials. Keywords evolve between Premium-class cores and Meta; a rule type that worked in a 2019 gist might be renamed or require a feature flag. When examples fail to parse, compare against the official sample for your exact version.

Mixing policy with morality. Rules route packets; they do not judge content. If you block trackers, do it with explicit REJECT rules or dedicated blocklists, not by hoping a random proxy group will “hide” telemetry—telemetry often uses resilient endpoints that need their own treatment.

Merging vendor profiles without losing your overrides

Real-world users rarely author an entire YAML from scratch. They import a remote profile, then attach a local patch file or use a GUI “prepend rules” field. Understand where your client inserts those fragments relative to the downloaded blob. Some apps prepend; others append; a few let you choose. If your override must win, prepending is usually safer than appending—unless you intentionally want a catch-all to stay at the very bottom.

When upstream providers ship breaking changes—renamed groups, removed nodes, new default MATCH targets—your local rules may still parse while semantics shift out from under you. After each subscription refresh, spot-check that critical group names still exist. Automated validation scripts exist in power-user circles, but even a manual diff of proxy-groups names against your custom rules section catches obvious regressions.

If you maintain separate files for “nodes from vendor” versus “policy I own,” consider generating the final config with a templating tool or a small script. That sounds heavy, yet it prevents the classic failure mode where a GUI merge silently duplicated a section and Clash now sees two conflicting definitions. For single-machine setups, a simple Makefile that concatenates in a known order is often enough.

Privacy, threat models, and honest expectations

Custom rules improve control and convenience: fewer captchas from hopping IPs, smoother streaming, safer split tunneling for LAN devices. They do not, by themselves, guarantee anonymity. Your proxy operator, DNS resolver, and destination sites each see parts of the story. Write rules with a clear goal—latency, region unlock, ad blocking, or corporate compliance—and avoid marketing language that promises “total privacy” from routing tweaks alone.

Process-based policies also surface metadata about which applications you run. Local logs may record executable names alongside connection metadata. If that sensitivity matters, rotate logs, restrict file permissions, and choose clients with sensible defaults for log retention. None of that replaces full-disk encryption or host hardening, but it aligns expectations with how diagnostic features actually work.

Where to go next

Custom rules turn Clash from a generic tunnel into a policy router aligned with how you actually work and play. Pair disciplined ordering with well-named proxy-groups, add process rules where domains lie, and treat DNS visibility as part of the design—not an afterthought. Compared with one-size-fits-all VPN clients, that combination is what makes rule-based tools worth the learning curve.

If you are new to installation paths and profile layout, start with our Clash tutorial, then iterate on rules with confidence. When you are ready to install or refresh a maintained client with modern core features and readable diagnostics, use our download page as the primary path—Download Clash for free and experience the difference.