Clash 自訂規則語法與優先順序:DOMAIN-SUFFIX、IP-CIDR 與 MATCH 的比對順序
逐條拆解 rules 段的寫法與比對順序:從 DOMAIN、DOMAIN-SUFFIX、IP-CIDR 到 GEOIP 與 MATCH 兜底,說明由上而下的短路比對機制與常見錯誤寫法。
rules 段的三段式寫法與由上而下的短路比對
Clash 與 mihomo 的規則全部寫在設定最上層的 rules: 陣列裡,每條規則是一個 YAML 清單項目,結構固定為三段:規則類型,比對值,策略名。第一段決定用哪種方式比對,第二段是比對目標,第三段是命中後要走的出口。出口可以寫 DIRECT(直連)、REJECT(拒絕),也可以寫 proxy-groups 裡定義過的任意策略組名稱。部分類型還接受第四段參數,目前最常用的是 no-resolve。
rules:
- DOMAIN,api.github.com,Proxy
- DOMAIN-SUFFIX,github.com,Proxy
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
- GEOIP,CN,DIRECT
- MATCH,Proxy
比對過程是由上而下的短路比對:內核從第一條開始逐條比對,一旦命中就立刻結束,後面的規則不再參與。也就是說,規則順序本身就是優先順序,內核不會因為某條規則寫得比較具體就自動把它提前。把 DOMAIN-SUFFIX,github.com,Proxy 排在 DOMAIN,api.github.com,DIRECT 前面,後者永遠不會生效。
MATCH 不帶比對值,任何流量都會命中它。一旦它被寫在規則表中間,後面所有規則都成了永遠不會執行的無效程式碼。規則改動後先檢查這一條。
策略名稱必須與 proxy-groups 裡定義的完全一致,包含大小寫;可以直接寫的只有 DIRECT 和 REJECT,其餘都是自訂群組名稱。
網域規則:DOMAIN、DOMAIN-SUFFIX 與 DOMAIN-KEYWORD 的界線
網域類規則分三個層級,比對範圍依序放寬,撰寫之前先確認要涵蓋的是單一主機還是整個網站:
| 寫法 | 命中範例 | 未命中範例 |
|---|---|---|
DOMAIN,api.github.com |
api.github.com | github.com、cdn.api.github.com |
DOMAIN-SUFFIX,github.com |
github.com、api.github.com | raw.githubusercontent.com、github.com.cn |
DOMAIN-KEYWORD,github |
github.com、githubassets.com、mygithub.io | gitlab.com |
DOMAIN 是精確比對,只認完全相同的網域。DOMAIN-SUFFIX 依網域層級邊界比對:github.com 本身,以及以 .github.com 結尾的主機名稱都算命中,但 github.com.cn 不算——它既不完全相等,也不以 .github.com 結尾。很多人把它理解成字串後綴,於是誤以為 github.com.cn、notgithub.com 會被這條規則帶走,實際上都不會。
DOMAIN-KEYWORD 是純子字串比對,github 會同時命中 githubusercontent.com 和 mygithub.io。它適合網域變體多、後綴不一致的服務,但關鍵字越短誤傷越大,app、api、cloud 這類詞不建議直接當成關鍵字。
mihomo 另外支援 DOMAIN-REGEX,用正規表達式描述複雜模式。每條連線都要跑一次正規表達式,規則條數多時開銷相當可觀,通常只在少數必要情境下使用。
IP 類規則:IP-CIDR、GEOIP 與 no-resolve 的解析成本
IP 類規則包括 IP-CIDR、IP-CIDR6、SRC-IP-CIDR 和 GEOIP,比對對象是 IP 位址。而瀏覽器和客戶端發起的連線通常只帶網域,所以內核遇到網域請求時,會先做一次 DNS 解析取得 IP,再拿去比對。
這次解析有兩個副作用:每條連線多一次查詢;解析結果取決於 dns 段的設定,可能與撰寫規則時的預期不一致。
no-resolve 用來關閉這個行為。加上它之後,如果目前請求攜帶的是網域,內核不會為了比對這條規則去解析,而是直接判定不吻合並繼續往下走。
rules:
- IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- IP-CIDR,100.64.0.0/10,DIRECT,no-resolve
- IP-CIDR6,fc00::/7,DIRECT,no-resolve
- GEOIP,CN,DIRECT
- MATCH,Proxy
內網網段、回送位址和電信業者保留網段基本上都靠 IP 規則處理,加上 no-resolve 後不會產生額外解析。GEOIP,CN,DIRECT 這類規則必須取得真實 IP 才能判斷歸屬,一般不加 no-resolve,讓它正常觸發解析。
在 dns.enhanced-mode: fake-ip 下要特別注意:應用程式拿到的是內核偽造的位址(預設落在 198.18.0.1/16),這些位址不代表真實位置,不要用 IP 規則去比對。把網域類規則排在 IP 類規則前面,能少跑一輪解析。
GeoIP 資料檔缺失或載入失敗時,所有 GEOIP 規則都不會命中,流量會一路掉到 MATCH,結果連中國大陸的網站也走了代理。檢查規則之前,先確認資料檔存在。
連接埠、行程與來源網段:細部規則怎麼排
DST-PORT、SRC-PORT 依連接埠比對,PROCESS-NAME、PROCESS-PATH 依發起連線的行程比對,SRC-IP-CIDR 比對發起連線的本機位址。這幾類規則一般放在網域規則之後、MATCH 之前。
rules:
- PROCESS-NAME,Telegram.exe,Proxy
- PROCESS-PATH,/usr/bin/curl,Proxy
- SRC-IP-CIDR,192.168.1.0/24,DIRECT,no-resolve
- DST-PORT,22,DIRECT
- MATCH,Proxy
行程類規則需要先判斷連線屬於哪個行程,單條比對成本高於純網域比對,放在規則表較前面的位置比較划算。PROCESS-NAME 在 Windows 和 macOS 上可直接使用;Linux 上行程資訊受權限限制,多數情境用 PROCESS-PATH 更可靠。
SRC-IP-CIDR 常用來把虛擬機網路卡、Docker 網路橋接這類來源的流量單獨導向直連,並搭配 no-resolve 使用。連接埠規則的涵蓋範圍很廣,DST-PORT,22,DIRECT 會影響所有目標連接埠為 22 的連線,撰寫前先確認它不會把後面原本該走代理的流量攔截掉。
RULE-SET 與 rule-providers:把規則移出主設定
規則條數上百之後,全部堆在主設定裡既難閱讀也難更新。rule-providers 把規則拆成獨立檔案,rules 裡用 RULE-SET 引用:
rule-providers:
ad-block:
type: file
behavior: domain
path: ./ruleset/ad-block.yaml
rules:
- RULE-SET,ad-block,REJECT
- MATCH,Proxy
behavior 決定檔案內容的格式:domain 是純網域清單,ipcidr 是網段清單,classical 是完整的規則語法。RULE-SET 後面的名稱必須與 rule-providers 下的鍵完全一致,寫錯時設定載入會直接報錯。
如果規則集需要定期從遠端更新,把 type 換成 http,補上 url 和 interval 兩個欄位,interval: 86400 表示每 24 小時拉取一次。規則集檔案損毀或格式不符時,整條 RULE-SET 會失效——把 MATCH 兜底寫好,發生問題時的行為至少是可預期的。
六種常見錯誤寫法與日誌檢查路徑
依出現頻率由高到低排列,規則不生效時可依序對照:
MATCH寫在中間:後面所有規則失效,是檢查時最容易忽略的一點。- 把
DOMAIN-SUFFIX當成字串後綴:github.com.cn、notgithub.com都不會命中,要涵蓋變體得改用DOMAIN-KEYWORD或DOMAIN-REGEX。 DOMAIN-KEYWORD用得太寬:短關鍵字會連帶命中無關網域,把整條連線鏈路拖進代理。- IP 規則漏寫
no-resolve:每條網域連線都多一次解析,延遲和 DNS 負載都會上升。 - 策略名稱大小寫不一致:
proxy-groups裡叫Proxy,規則裡寫成proxy,設定驗證會失敗。 - GeoIP 資料檔缺失:
GEOIP規則全部落空,流量掉到MATCH。
排解路徑:在設定裡開啟 external-controller: 127.0.0.1:9090,用面板查看每條連線實際命中的規則;內核日誌會印出 match DomainSuffix(github.com) using Proxy 這樣的記錄,直接指出是哪一條生效。修改規則後重新載入設定,再重現一次同樣的連線即可確認。
規則表本質上是一份依序執行的判定清單。寫完之後從上往下讀一遍,確認每一條都不會被前面的寬鬆規則攔走,再把 MATCH 放在最後,規則部分基本上就不會出問題。