Skip to main content
EN

Home / Blog / Rules & Routing

Remote rule sets with rule-providers — behavior, format and update strategy

Rules & Routing2026-07-211265 words3 min read
Remote rule sets with rule-providers — behavior, format and update strategy

Writing hundreds of DOMAIN-SUFFIX rules by hand is impractical, and it cannot keep up with how sites change. rule-providers lets you reference externally maintained lists that refresh on a schedule.

The basic structure

Two steps: define the provider, then reference it from rules.

rule-providers:
  cn-domain:
    type: http
    behavior: domain
    format: text
    url: "https://example.com/rules/cn-domain.txt"
    path: ./ruleset/cn-domain.txt
    interval: 86400
    proxy: DIRECT

rules:
  - RULE-SET,cn-domain,DIRECT
  - MATCH,PROXY

Field by field

FieldValuesMeaning
typehttp / file / inlineRemote, local file, or written inline
behaviordomain / ipcidr / classicalMust match the file's contents
formattext / yaml / mrsFile format
urlAddress of the rule fileRequired when type is http
pathLocal cache pathRelative to the config directory
intervalSecondsUpdate interval; 86400 = one day
proxyPolicy nameWhich route to use when downloading the file

behavior: the three types are not interchangeable

This is where things go wrong most often — an incorrect behavior produces no error, the rules just silently do nothing.

What file contents each behavior expectsdomaina plain one domain or +.domain per lineno IPs and no rule typesthe most commonipcidra plain one CIDR per lineno domainsclassicalfull ruleach line is "type,value"any type may be mixed inmost flexible but slowest
When you get a rule file, open it and look at the contents before choosing a behavior

domain

The file looks like this:

google.com
+.youtube.com
www.github.com

The +. prefix is equivalent to DOMAIN-SUFFIX (all subdomains included); without it the match is exact.

ipcidr

1.0.1.0/24
8.8.8.8/32
2001:4860::/32

classical

DOMAIN-SUFFIX,google.com
DOMAIN-KEYWORD,youtube
IP-CIDR,8.8.8.8/32,no-resolve
PROCESS-NAME,Telegram.exe

Note: a classical file contains no policy names. The policy comes from the RULE-SET line that references it.

format: text, yaml and mrs

text: plain text, one entry per line, # starts a comment. The most common.

yaml:

payload:
  - google.com
  - "+.youtube.com"

mrs: a binary format specific to Mihomo, small and fast to load. It supports only domain and ipcidr, not classical.

Loading cost of the three formats (relative, same 100,000-entry domain list)mrs binaryfastest, lowest memorytext plainmoderate parse timeyamlneeds a full YAML parseThe difference is negligible for small rule sets; above ten thousand entries mrs pulls clearly ahead

For very large rule sets (tens of thousands of entries and up), prefer the mrs version where the source provides one.

interval: how often to update

Kind of rule setSuggested interval
Regional domain / IP lists86400 (one day)
Ad-blocking lists86400
Streaming unlock lists43200 (half a day)
Small lists you maintain yourself3600 or longer

Setting it very short achieves nothing — these lists do not change several times a day, and you just add load to whoever hosts them.

A complete working configuration

A combination of rule sets that covers the common needs:

rule-providers:
  # ad blocking
  reject:
    type: http
    behavior: domain
    format: text
    url: "https://example.com/reject.txt"
    path: ./ruleset/reject.txt
    interval: 86400
    proxy: DIRECT

  # domains that should go direct
  direct:
    type: http
    behavior: domain
    format: text
    url: "https://example.com/direct.txt"
    path: ./ruleset/direct.txt
    interval: 86400
    proxy: DIRECT

  # domains that need a proxy
  proxy:
    type: http
    behavior: domain
    format: text
    url: "https://example.com/proxy.txt"
    path: ./ruleset/proxy.txt
    interval: 86400
    proxy: DIRECT

  # in-region IP ranges
  cncidr:
    type: http
    behavior: ipcidr
    format: text
    url: "https://example.com/cncidr.txt"
    path: ./ruleset/cncidr.txt
    interval: 86400
    proxy: DIRECT

  # loopback and private ranges
  lancidr:
    type: http
    behavior: ipcidr
    format: text
    url: "https://example.com/lancidr.txt"
    path: ./ruleset/lancidr.txt
    interval: 86400
    proxy: DIRECT

rules:
  - RULE-SET,lancidr,DIRECT,no-resolve
  - RULE-SET,reject,REJECT
  - RULE-SET,proxy,🚀 Select
  - RULE-SET,direct,DIRECT
  - RULE-SET,cncidr,DIRECT,no-resolve
  - GEOIP,CN,DIRECT
  - MATCH,🐟 Fallthrough

Mind the order: private → blocking → proxy → direct → IP ranges → geographic catch-all → MATCH.

The proxy list goes before the direct list because some domains appear in both, and giving the proxy list priority matches what people expect.

inline: small rule sets written in place

When you do not want a separate file for three entries:

rule-providers:
  my-work:
    type: inline
    behavior: domain
    payload:
      - "+.mycompany.com"
      - "+.internal.corp"
      - "gitlab.internal"

file: using a local file

For rules you maintain yourself:

rule-providers:
  personal:
    type: file
    behavior: classical
    path: ./ruleset/personal.list

With interval set, some core versions re-read the file periodically. Reloading the config after editing is the reliable route.

What to check when a rule set does nothing

Order of investigationCheck that behavior matches the file contents (by far the most common mistake)Check whether the file at path actually downloadedSearch the log for the provider name and look for download failuresDoes the url open directly in a browserIs the RULE-SET line being intercepted by an earlier ruleHas MATCH accidentally been placed above the RULE-SET lines

Inspect the cached file

The file named by path sits under your config directory; just open it:

  • File missing → download failed; check the url and the proxy setting
  • File contains HTML → the url returned a web page (a 404 page, for instance); the address is wrong
  • File looks fine but rules do nothing → behavior mismatch

On Windows the config directory is usually:

%APPDATA%\io.github.clash-verge-rev.clash-verge-rev\

Read the log

Switch the log level to debug and search for the provider name; you will see the whole download-and-load sequence:

INFO Start initial provider cn-domain
INFO Provider cn-domain loaded, 12043 rules

Seeing loaded, N rules means it worked. If N is 0, the behavior or format is wrong.

Performance considerations

Resource use of large rule sets1Memoryroughly a few MB per ten thousand domain rules; at the hundred-thousand scale it is worth watching2First loaddownloading and parsing adds a few seconds to startup3Match costdomain sets use a suffix tree and are close to O(1); classical compares line by line and is much slower4Adviceprefer mrs-format domain

If your rule sets total more than a hundred thousand entries and the device is modest (a soft router, an old phone), consider:

  • Switching to the mrs format
  • Dropping rule sets you do not use (streaming platforms you never watch)
  • Replacing some domain lists with GEOSITE, which uses the core's built-in database and needs no extra download

In short

  • behavior must correspond to the file contents; get it wrong and it fails silently
  • Add no-resolve when referencing IP-based rule sets
  • Give each provider a proxy field so updates do not fail
  • Rule-set order: private → blocking → proxy → direct → IP → GEOIP → MATCH
  • When nothing happens, first look at what is actually in the cached file

Next, a practical build: company intranet, direct local access and overseas proxy coexisting.


Related docs

Routing individual programs with PROCESS-NAME rules
Rules & Routing Routing individual programs with PROCESS-NAME rules

The complete picture for per-process routing: enabling find-process-mode, finding the process name, PROCESS-NAME versus PROCESS-PATH, the performance cost, and why a rule you wrote does nothing.

2026-06-221224 words3 min read