自訂 Xray 路由規則:domain、IP、geosite 語法與比對優先順序

整理 domain、full、regexp、geosite、geoip 等規則前綴用法,說明路由規則的比對順序,以及 domainStrategy 對 IP 比對的影響。

本文重點

本文適合準備編輯 Xray 路由規則的使用者閱讀。從網域與 IP 的輸入差異切入,說明規則語法、欄位組合與解析策略,並提供可供核對的 routing 設定片段。讀完後即可判斷連線為何走直連、代理或阻斷,以及修改規則後該查看哪些記錄。範例使用保留測試網域,不涉及真實服務憑證。

路由輸入與出站標籤

Xray 路由負責選擇出站,不會替應用程式建立請求。應用程式的連線先進入 inbounds,路由再讀取目標位址、連接埠與網路類型等資訊,並將連線交給對應的 outbounds。v2rayN 是用戶端介面;本文程式碼描述的是 Xray 核心的 JSON 設定,不能直接當成訂閱連結匯入。

入站連線 目標資訊 規則比對 選擇出站 遠端連線

每條規則的 outboundTag 都必須對應到現有出站的 tag。以下使用的 direct、proxy、block 都是自訂標籤,不會因為填上名稱就自動啟用功能。通常分別對應直連出站、已設定的代理出站與阻斷出站。

domain、full 與 regexp 的差異

網域比對內容放在規則的 domain 陣列中。這裡的 domain 既是欄位名稱,也可以是陣列元素的比對前綴。欄位名稱表示「檢查網域」,前綴則表示「比對方式」。以下的 example.test 是保留測試網域,適合用來說明語法,不代表可直接連線的服務。

陣列元素比對方式比對範圍
full:api.example.test完整網域比對只比對該主機,不包含其子網域。
domain:example.test網域及子網域比對包含根網域與 api.example.test,不包含 notexample.test。
example一般字串比對以子字串比對,涵蓋範圍通常比網域後綴比對更廣。
regexp:^api[0-9]+\\.example\\.test$正規表示式比對這是 JSON 字串格式,反斜線必須跳脫。
geosite:cn網域集合比對讀取本機網域資料檔中的指定集合。

只想讓單一主機走代理,優先使用 full:。需要涵蓋整個網域樹時,使用 domain:。不要把 *.example.test 當成通用萬用字元寫進一般網域項目;若要達到萬用字元效果,應使用網域後綴比對或明確的正規表示式。

{
  "type": "field",
  "domain": [
    "full:api.example.test",
    "regexp:^edge[0-9]+\\.example\\.test$"
  ],
  "outboundTag": "proxy"
}

上例中的兩個陣列元素採「或」的關係,符合任一項即可。正規表示式中的句點必須表示為字面句點;因此在 JSON 中要寫成 \\.。開頭的 ^ 與結尾的 $ 用來限制比對範圍,避免意外符合前後帶有其他字元的主機名稱。

ip、geoip 與 geosite 資料集

ip 欄位用來檢查目標 IP,可填入單一位址、CIDR 網段或 geoip: 集合。網域集合放在 domain 中,IP 集合則放在 ip 中。geosite 與 geoip 不是線上查詢服務,也不能互換欄位。

{
  "type": "field",
  "ip": [
    "192.168.0.0/16",
    "10.0.0.0/8",
    "fc00::/7",
    "geoip:private"
  ],
  "outboundTag": "direct"
}

這段範例明確列出幾個私有網段,並以 geoip:private 集合補足涵蓋範圍。部分範圍會重疊,目的是方便對照語法;實際設定可依所用資料檔內容精簡。192.168.0.0/16 代表一個網段,不是只比對 192.168.0.0 這個位址。

比對優先順序與欄位組合

routing.rules 會依陣列順序逐條檢查,第一條符合條件的規則就會決定出站。規則越具體,不代表核心會自動將它排在越前面。前面的寬泛規則可能提早結束比對,讓後面的例外規則沒有機會生效。

{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "domain": ["full:api.example.test"],
        "outboundTag": "proxy"
      },
      {
        "type": "field",
        "domain": ["domain:example.test"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "network": "tcp,udp",
        "outboundTag": "proxy"
      }
    ]
  }
}

在這段設定中,api.example.test 走代理,www.example.test 走直連,其餘 TCP、UDP 連線則由最後一條規則交給代理。若將第二條移到第一條之前,介面網域會先符合網域樹規則,改走直連。

欄位組合邏輯關係實際意義
同一個 domain 陣列中的多個項目或符合其中任一項,就符合該欄位條件。
同一個 ip 陣列中的多個網段或位址落在任一網段內,就符合該欄位條件。
同一條規則中的 domain 與 port且網域與連接埠必須同時符合。
同一條規則中的 domain 與 ip且不是網域或 IP 擇一符合;IP 條件也取決於目標位址資訊與解析策略。

結論:例外規則應排在集合規則之前

先寫單一主機的特殊處理,再寫網域樹與分類集合,最後才寫兜底規則。若要達成「網域符合或 IP 符合就直連」,應拆成兩條規則,而不是把兩種欄位放進同一條規則。

domainStrategy 如何影響 IP 比對

routing.domainStrategy 決定路由階段是否為網域補上 IP 資訊。它不是用來調整規則順序的開關,也不是指定 DNS 伺服器位址的欄位。即使設定中有 dns 區段,路由也不會因此自動對每個網域執行 IP 比對。

策略路由階段的行為注意事項
AsIs不會為了路由比對而主動解析網域。若目標本身就是 IP,仍可比對 IP 規則。
IPIfNonMatch網域未符合規則時,才嘗試解析並比對含有 IP 條件的規則。若第一輪已符合寬泛的兜底規則,就不會因為未符合而觸發解析。
IPOnDemand遇到需要 IP 資訊的規則時,會嘗試解析網域後繼續判斷。排在前面的 IP 規則可能增加對解析的依賴,也可能影響後續網域規則是否有機會執行。

常見的誤解是:選擇 IPIfNonMatch,先寫 geoip:cn,最後再寫一條涵蓋所有 TCP、UDP 連線的代理規則,就以為所有網域都會先依地區 IP 分流。對網域目標而言,第一輪可能直接符合最後的代理規則;既然已經符合,就沒有「未符合後再解析」的機會。

  1. 若主要想依網域集合分流:先設定清楚的網域規則,並確認只有 IP 的請求該如何處理。
  2. 若要等網域沒有規則可符合時才依 IP 判斷:使用 IPIfNonMatch,並檢查是否有通用規則提早符合。
  3. 若希望較前面的 IP 條件也參與網域請求的判斷:評估使用 IPOnDemand,並確認 DNS 回傳結果與規則順序符合預期。

路由解析取得的 IP,不應直接視為建立連線時最終使用的位址。出站是否繼續使用網域、在哪個階段解析,也會受到所用協定與出站設定影響。排查時應分別確認「路由為何選中這個標籤」與「連線最後前往哪裡」。

依連接埠建立最小驗證案例

不要一次匯入數十條分類規則,再觀察網頁能不能開啟。先使用明確的入站連接埠、一條限制連接埠的網域規則,以及容易辨識的出站標籤,驗證比對流程。以下三個數值是測試設定中的明確參數,不是所有用戶端版本的預設值。

10808
本例的本機 SOCKS 入站連接埠
443
本例網域規則的目標連接埠
53
傳統 DNS 常用的目標連接埠
{
  "type": "field",
  "domain": ["full:api.example.test"],
  "port": "443",
  "network": "tcp",
  "outboundTag": "proxy"
}

這條規則只會比對目標網域、目標連接埠 443 與 TCP 三者同時符合的連線。它不會比對同一主機的 80 連接埠,也不會比對 UDP 443。port 檢查的是目標連接埠,不是本機 SOCKS 監聽的 10808。若瀏覽器使用以 UDP 為基礎的傳輸方式,不能用這條 TCP 規則推斷結果。

  1. 先備份目前可用的設定,並確認核心已讀取新的路由設定,而不是仍在執行舊的程序。
  2. 確認本機入站確實監聽 127.0.0.1:10808;若使用其他連接埠,請以實際設定為準。
  3. 在測試環境中,為保留網域準備解析或嗅探輸入。若沒有準備測試服務,只檢查核心能否載入規則,不要把連線失敗當成路由失敗。
  4. 若要比較 DNS 行為,可暫時提高記錄層級並重複一次請求,核對接收目標、DNS 查詢與出站標籤;測試完成後請恢復原本的記錄層級。
  5. 每次只調整一項:先調整規則順序,再調整 domainStrategy,最後才加入資料集。保留每個階段的結果,避免多項變更互相干擾。

v2rayN 路由疑難排解

在 v2rayN 中調整路由後,也要確認目前選用的路由設定與核心類型。本文說明的是 Xray 的欄位語意;介面中的路由編輯項目可能由用戶端轉換後寫入產生的設定,不要將整段 JSON 隨意貼進只接受網域項目的輸入框。

加了 full 規則,為什麼還是走原本的出站?

先將該規則移到寬泛的網域集合規則與兜底規則之前,再重新載入設定。查看記錄中的目標是否仍是網域;若目標只有 IP,請確認應用程式是否自行解析,以及入站嗅探是否確實提供可用的網域。

geosite 規則儲存後,核心為什麼無法啟動?

查看核心啟動記錄,分別確認資料檔能否讀取、集合名稱是否存在,以及規則是否放在正確欄位。先用一條 full: 規則驗證路由,再還原集合規則,即可區分是語法問題還是資料檔問題。

設定了 IPIfNonMatch,為什麼沒有看到預期的 DNS 查詢?

確認第一輪是否已符合網域規則或 network: tcp,udp 兜底規則。若已符合,就不符合「未符合」的觸發條件。若需要讓較前面的 IP 條件參與判斷,再評估 IPOnDemand,不要只靠移動 IP 規則來猜測結果。

同一條規則中同時寫 domain 和 ip,為什麼符合的連線變少了?

這兩個欄位必須同時符合。若原本要表達的是網域集合或位址集合任一符合就直連,請拆成兩條規則,並使用相同的 outboundTag。接著分別以網域目標與 IP 目標進行測試。

修改產生的設定後,重新啟動用戶端又恢復原狀,該怎麼辦?

自動產生的設定可能會在切換節點或重新啟動核心時重建。請透過用戶端提供的路由設定入口儲存規則,或使用用戶端支援的自訂設定方式。重新啟動後再次核對核心實際載入的內容;曾在編輯器中成功儲存,不代表設定已生效。

請固定依照以下順序核對:輸入目標是什麼、規則是否符合、哪條規則最先符合、標籤是否存在、出站能否連線。網頁能開啟,只能證明一次存取成功,不能單獨證明分流正確。保留最小測試規則與對應記錄,比同時切換多種路由模式更容易找出問題。

下載v2rayN