Clash カスタムルールの構文と優先順位:DOMAIN-SUFFIX、IP-CIDR と MATCH のマッチ順
rules セクションの書き方とマッチ順を解説。DOMAIN、DOMAIN-SUFFIX、IP-CIDR から GEOIP、MATCH のフォールバックまで、上から順に評価される仕組みとよくある記述ミスを整理します。
rules セクションの3段構成と上から順に評価されるマッチング
Clash と mihomo のルールはすべて設定ファイル最上位の rules: 配列に記述します。各ルールは YAML のリスト項目で、構造は ルールタイプ,マッチ値,ポリシー名 の3段で固定です。1段目がマッチ方式、2段目がマッチ対象、3段目が一致したときに通す出口を指定します。出口には DIRECT(直接接続)、REJECT(拒否)のほか、proxy-groups で定義した任意のプロキシグループ名を書けます。一部のタイプは4段目のパラメータも受け付け、現在もっともよく使われるのは 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
マッチングは上から順に評価されるショートサーキット方式です。カーネルは1行目から順に照合し、一致した時点で即座に確定して、それ以降のルールは評価しません。つまり、ルールの並び順そのものが優先度であり、記述がより具体的だからといって自動的に前へ移動されることはありません。DOMAIN-SUFFIX,github.com,Proxy を DOMAIN,api.github.com,DIRECT より前に置くと、後者は永久に適用されません。
MATCH はマッチ値を持たず、あらゆる通信がここで一致します。ルール一覧の途中に書いてしまうと、それ以降のルールはすべて実行されない死んだ記述になります。ルールを変更したら、まずこの1行を確認してください。
ポリシー名は proxy-groups で定義した名前と大文字・小文字まで完全に一致させる必要があります。そのまま書けるのは DIRECT と REJECT だけで、それ以外はすべてカスタムのグループ名です。
ドメイン系ルール:DOMAIN・DOMAIN-SUFFIX・DOMAIN-KEYWORD の境界
ドメイン系ルールは3段階あり、マッチ範囲はこの順に広がります。書く前に、対象が単一のホストなのかサイト全体なのかを確認しましょう。
| 書き方 | 一致する例 | 一致しない例 |
|---|---|---|
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 に対応し、正規表現で複雑なパターンを表現できます。ただし接続ごとに正規表現を1回実行するため、ルール数が多いと負荷が無視できません。通常はごく限られた場面でのみ使います。
IP 系ルール:IP-CIDR・GEOIP と no-resolve の解決コスト
IP 系ルールには IP-CIDR、IP-CIDR6、SRC-IP-CIDR、GEOIP があり、マッチ対象は IP アドレスです。一方、ブラウザやクライアントが張る接続は通常ドメイン名しか持ちません。そのためカーネルはドメイン名のリクエストに遭遇すると、まず DNS 解決を行って IP を取得し、それから照合します。
この解決には2つの副作用があります。接続ごとにクエリが1回増えること、そして解決結果が 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 系ルールより前に並べると、解決の1巡を減らせます。
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
プロセス系ルールはまず接続がどのプロセスに属するかを特定する必要があり、1件あたりの照合コストは単純なドメイン比較より高くなります。そのためルール一覧のなるべく前の方に置くほうが効率的です。PROCESS-NAME は Windows と macOS ではそのまま使えますが、Linux ではプロセス情報が権限の制約を受けるため、多くの場面では PROCESS-PATH のほうが確実です。
SRC-IP-CIDR は、仮想マシンのネットワークアダプタや Docker ブリッジなどからの通信を個別に直接接続へ振り分ける用途でよく使われ、no-resolve と組み合わせます。ポートルールは適用範囲が非常に広く、DST-PORT,22,DIRECT は宛先ポートが 22 のすべての接続に影響します。後ろでプロキシを通すべき通信を切り落とさないか、書く前に確認してください。
RULE-SET と rule-providers:ルールをメイン設定の外へ
ルールが100件を超えると、すべてをメイン設定に置くのは読みにくく、更新も大変です。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 の2つのフィールドを追加します。interval: 86400 は24時間ごとに取得するという意味です。ルールセットのファイルが壊れていたり形式が合っていなかったりすると、その RULE-SET 全体が機能しなくなります。MATCH のフォールバックをきちんと書いておけば、障害時の挙動は少なくとも予測可能になります。
よくある6つの記述ミスとログからの調査手順
出現頻度の高い順に並べています。ルールが効かないときは上から順に確認してください:
MATCHを途中に書いている:それ以降のルールがすべて無効になります。調査時に最も見落とされやすいポイントです。DOMAIN-SUFFIXを文字列のサフィックスと誤解している:github.com.cnやnotgithub.comは一致しません。バリエーションをカバーしたい場合はDOMAIN-KEYWORDかDOMAIN-REGEXに切り替えてください。DOMAIN-KEYWORDの範囲が広すぎる:短いキーワードは無関係なドメインまで巻き込み、経路全体をプロキシに引き込みます。- IP ルールに
no-resolveを書き忘れている:ドメイン名の接続ごとに解決が1回増え、遅延と DNS の負荷が上がります。 - ポリシー名の大文字・小文字が一致していない:
proxy-groupsではProxyなのに、ルール側でproxyと書くと設定の検証に失敗します。 - GeoIP データファイルが欠けている:
GEOIPルールがすべて空振りし、通信はMATCHまで落ちます。
調査手順:設定で external-controller: 127.0.0.1:9090 を有効にし、ダッシュボードで各接続が実際にどのルールに一致したかを確認します。カーネルログには match DomainSuffix(github.com) using Proxy のような記録が出力され、どのルールが効いたかが直接わかります。ルールを変更したら設定を再読み込みし、同じ接続をもう一度発生させて確認してください。
ルール一覧は、結局のところ上から順に実行される判定リストです。書き終えたら上から下へ読み直し、どのルールも前にある広いルールに横取りされないことを確かめ、MATCH を最後に置けば、ルール部分で問題が起きることはほぼなくなります。