客户端与分流规则:Mihomo 配置
真正决定体验的不是节点,是规则:什么直连、什么走代理、DNS 交给谁。
学完这节你能做到
- 读懂并改写一份 Mihomo/Clash 配置的关键段落
- 设计一套「国内直连、技术站点走代理」的规则顺序
- 判断该用系统代理还是 TUN 模式
建议先学
跳过这几节会看不懂本节的部分推导
决定体验的是规则,不是节点
换了更快的节点却觉得更卡,往往是因为该直连的流量也绕出去了:内网服务、国内站点、 公司仓库全走一趟远路。规则配对了,慢的那部分才会消失。
这一节以 Mihomo(原 Clash.Meta,Clash Verge Rev 内置的核心)的配置为例, 拆开真正需要理解的四段:端口、DNS、代理组、规则。
端口:三个还是一个
port: 7890 # HTTP 代理端口
socks-port: 7891 # SOCKS5 代理端口
mixed-port: 7892 # 一个端口同时支持 HTTP 与 SOCKS5 ← 推荐只用这个
allow-lan: false # 是否允许局域网其它设备连过来
bind-address: '*'
mode: rule # rule(按规则)/ global(全部走代理)/ direct(全部直连)
log-level: info
external-controller: 127.0.0.1:9090 # 管理 API,GUI 和排障面板靠它
mixed-port 在一个端口上同时认 HTTP 和 SOCKS5:客户端配哪种都能用,
少一个要记的端口。allow-lan: true 要谨慎——等于在局域网里开了一个开放代理,
至少配合 authentication 和防火墙使用。
mode 这三个值在排障时很有用:怀疑规则写错时先切 global 试一下,
能通就说明问题在规则而不在节点。
DNS:fake-ip 到底解决了什么
dns:
enable: true
listen: 0.0.0.0:1053
ipv6: false
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
fake-ip-filter: # 这些域名不给假 IP,走真实解析
- '*.lan'
- '*.local'
- '*.internal'
- '*.svc.cluster.local'
- 'localhost.ptlogin2.qq.com'
nameserver: # 国内解析器,用于解析国内域名
- 223.5.5.5
- 119.29.29.29
fallback: # 境外解析器,走加密协议
- https://1.1.1.1/dns-query
- tls://8.8.8.8:853
nameserver-policy: # 按域名指定解析器
'+.internal': 10.0.0.53
'+.cluster.local': 10.96.0.10
fake-ip 的原理:客户端查询域名时,立刻返回一个 198.18.x.x 的假地址(不做真实解析)。
客户端拿着这个假 IP 发起连接,Mihomo 收到后反查出原始域名,再按域名规则决定走哪条链。
它解决的是一个真实的两难:
| 模式 | 问题 |
|---|---|
| 不用 fake-ip | 每次连接前要先等真实 DNS 解析,慢,而且解析结果可能是错的 |
| fake-ip | 解析零延迟,且决策依据是域名而不是 IP,规则更准 |
既然连接时用的是假 IP,IP-CIDR 和 GEOIP 这类规则拿到的就是 198.18.x.x,匹配不上真实地理位置。
Mihomo 的处理是:域名类规则优先匹配,匹配不上时才会去做真实解析再走 IP 规则。 但你要显式配合两件事:
- 需要按 IP 匹配却不希望触发解析的规则,加
no-resolve(比如IP-CIDR,10.0.0.0/8,DIRECT,no-resolve) - 内网域名、集群域名一定要进
fake-ip-filter,否则拿到假 IP 后压根连不上
第二条是 K8s 环境里最常见的坑:开了 fake-ip 之后 kubectl 突然不好用了,
因为 *.cluster.local 也被发了假地址。
代理组:三种最常用
proxy-providers: # 订阅:自动拉取节点列表
main:
type: http
url: "https://example.com/subscribe"
interval: 86400 # 每天更新
health-check:
enable: true
url: https://www.gstatic.com/generate_204
interval: 300
proxy-groups:
- name: 自动选择
type: url-test # 按延迟自动选最快的
use: [main]
url: https://www.gstatic.com/generate_204
interval: 300
tolerance: 50 # 差距小于 50ms 不切换,避免反复抖动
- name: 兜底
type: fallback # 按顺序用,前面的挂了才切下一个
use: [main]
url: https://www.gstatic.com/generate_204
interval: 300
- name: 代理
type: select # 手动选,GUI 里点
proxies: [自动选择, 兜底, DIRECT]
| 类型 | 行为 | 适合 |
|---|---|---|
url-test | 选延迟最低的 | 日常,配 tolerance 防抖 |
fallback | 按顺序,前面不可用才切 | 有明确优先级(比如自建优先) |
load-balance | 多个节点分摊 | 需要分散连接 |
select | 手动指定 | 排障,或明确要走某个出口 |
tolerance 很值得配:不加的话延迟每次测量抖动几毫秒就会切换节点,
表现是"连接莫名中断"——长连接会在切换时被掐掉。
规则:顺序就是一切
rule-providers:
cn-domains:
type: http
behavior: domain
url: "https://.../direct.txt"
interval: 86400
rules:
# 1. 局域网与内网:最先匹配,永远直连
- IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- DOMAIN-SUFFIX,internal,DIRECT
- DOMAIN-SUFFIX,cluster.local,DIRECT
# 2. 公司资产:明确直连,别让它绕出去
- DOMAIN-SUFFIX,corp.example.com,DIRECT
# 3. 技术站点:明确走代理
- DOMAIN-SUFFIX,github.com,代理
- DOMAIN-SUFFIX,githubusercontent.com,代理
- DOMAIN-SUFFIX,docker.io,代理
- DOMAIN-SUFFIX,k8s.io,代理
- DOMAIN-SUFFIX,nvidia.com,代理
- DOMAIN-KEYWORD,grafana,代理
# 4. 规则集:批量匹配
- RULE-SET,cn-domains,DIRECT
# 5. 国内 IP 直连(这一步会触发真实解析)
- GEOIP,CN,DIRECT
# 6. 其余的兜底
- MATCH,代理
规则从上往下匹配,命中即停。 所以顺序错了后面的规则永远不会生效——
比如把 GEOIP,CN,DIRECT 放在最前面,那么解析到国内 CDN 的境外服务也会被判直连。
几个常用类型:
| 类型 | 匹配 | 例 |
|---|---|---|
DOMAIN | 精确域名 | DOMAIN,api.github.com,代理 |
DOMAIN-SUFFIX | 域名后缀(含子域) | DOMAIN-SUFFIX,github.com,代理 |
DOMAIN-KEYWORD | 域名包含关键字 | DOMAIN-KEYWORD,grafana,代理 |
IP-CIDR | 目标 IP 段 | IP-CIDR,10.0.0.0/8,DIRECT,no-resolve |
GEOIP | IP 归属地 | GEOIP,CN,DIRECT |
PROCESS-NAME | 按进程名 | PROCESS-NAME,ssh,DIRECT |
RULE-SET | 引用规则集 | RULE-SET,cn-domains,DIRECT |
MATCH | 兜底,必须在最后 | MATCH,代理 |
先内网、再公司、再明确要代理的技术域名、再规则集、再 GEOIP、最后 MATCH。
这个顺序的逻辑是从最确定到最模糊:IP 段是确定的,域名后缀次之, GEOIP 依赖解析结果所以最不确定。把确定的放前面,能显著减少误判和不必要的解析。
TUN 模式:给不认代理的程序兜底
ping、dig、ssh、静态编译的二进制都不读 HTTP_PROXY 这类环境变量 ——
它们根本不知道代理的存在(两种代理差在哪 那节会讲清为什么)。
TUN 模式的解法是建一张虚拟网卡,把流量整体接走:
tun:
enable: true
stack: mixed # system / gvisor / mixed
auto-route: true # 自动装路由,把默认路由指过来
auto-detect-interface: true
dns-hijack:
- any:53 # 劫持所有 DNS 查询
| 方式 | 覆盖范围 | 代价 |
|---|---|---|
| 系统代理 | 只覆盖读代理设置的程序 | 轻量,不需要权限 |
| TUN 模式 | 覆盖所有程序,含 ICMP、DNS | 需要管理员权限;与其它 VPN 客户端容易抢路由 |
TUN 的 auto-route 会改默认路由。同时开着公司的 VPN 客户端(或 Tailscale、WireGuard)时,
两边都想接管默认路由,症状是内网时好时坏、或者两个都不通。
处理办法:
- 只让其中一个接管默认路由,另一个用明确的分流规则
- 或者把公司内网段在 Mihomo 规则里显式
DIRECT,并确认 TUN 没有劫持这些网段的 DNS - 排查时用
ip route show/route print看默认路由到底在谁手上
Clash Verge Rev:GUI 侧的实用功能
Clash Verge Rev 是基于 Tauri 的桌面客户端, 内置 Mihomo 内核,跨 Windows / macOS / Linux。对工程师有价值的几点:
- Merge 与 Script 增强层 —— 不用改订阅原文,用一份覆盖配置追加自己的规则。 订阅更新后自定义规则不会丢,这是最实用的功能
- 可视化节点与规则编辑 + 配置语法提示
- 系统代理守护 —— 被其它软件改掉后自动改回
- TUN 模式开关
- WebDAV 配置备份与同步 —— 多台机器保持同一套规则
- 发布分为 Stable / AutoBuild 通道,日常用 Stable
整网段分流:旁路由形态
TUN 模式解决的是"本机所有程序"。还有一类设备连 TUN 都装不了: 打印机、摄像头、老服务器上不读环境变量的程序、每次重建都要重配的 CI runner。 对这些设备只剩一条路:在流量必经的那一跳上做分流,两端都不知情。
家用场景的标准做法是旁路由 —— 不动主路由,加一台小设备作为"可选网关":
┌─────────────┐
设备 ──┤ 主路由 ├── 光猫 ── 外网
│ 192.168.1.1 │
└──────┬──────┘
┌──────▼──────────┐
│ 旁路由 .1.2 │ ← 跑 Mihomo,开 TUN 模式 auto-route
└──────────────────┘
需要走代理的设备:网关和 DNS 指向 192.168.1.2
不需要的设备:保持指向 192.168.1.1
# 旁路由上必须开转发,否则它只会把包丢掉
sysctl -w net.ipv4.ip_forward=1
# 让回程流量能出去(代理不接管全部出站时需要)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
规则本身不用另写一套 —— 旁路由上跑的就是这一节的 Mihomo 配置,
TUN 模式的 auto-route 会把路由接管的活干掉,不需要手写 REDIRECT / TPROXY 那套
iptables 规则。同一套思路搬到云上就是 NAT 实例:给整个私有子网做统一出口,
额外要记得关掉云厂商网卡上的"源/目的地址检查",否则转发怎么都不通。
① 它是新的单点。 所有指向它的设备,在它挂掉时全部断网 —— 而且症状是"能连 WiFi 但打不开任何网页",家里人不会自己排查。
② 转发路径变长。 流量走 设备 → 旁路由 → 主路由 → 外网,在两者之间多跑一趟。
千兆网内影响不大,但旁路由的 CPU 会成为整体吞吐的上限。
缓解办法是只把需要的设备指过去,而不是在主路由上把全网默认网关改成旁路由 —— 这也是旁路由比"直接刷主路由固件"更实用的原因:出问题时把网关改回去就恢复了。
排障:确认某个请求走了哪条链
# 1. 看核心日志(把 log-level 临时调到 debug)
tail -f ~/.config/clash-verge/logs/*.log
# 2. 通过管理 API 看实时连接:每条连接的 chains 字段就是它走的链路
curl -s http://127.0.0.1:9090/connections | jq '.connections[] | {host, chains, rule}'
# 3. 看某个域名会命中哪条规则(不实际发请求)
curl -s "http://127.0.0.1:9090/proxies" | jq -r '.proxies | keys[]'
GUI 的"连接"面板显示的就是第 2 条的内容。判断顺序:
rule字段 告诉你命中了哪条规则 → 规则写错就在这里暴露chains字段 告诉你实际走了哪个节点 → 代理组选择是否符合预期- 两个都对但还是不通 → 问题在节点或出口侧,切
global模式或换节点验证
检查点
开启 fake-ip 后,kubectl 访问集群突然失败。最可能漏了什么?
rules 里把 GEOIP,CN,DIRECT 放在了所有规则最前面,会发生什么?
同时开着 Mihomo 的 TUN 模式和公司 VPN 客户端,内网时好时坏。怎么处理?
这节课的落点
- 用
mixed-port一个端口搞定 HTTP 与 SOCKS5;allow-lan要配认证和防火墙 - fake-ip 让解析零延迟且按域名决策,代价是 IP 类规则失效——
内网域名进
fake-ip-filter,IP 规则加no-resolve - 代理组:
url-test配tolerance防抖,fallback有优先级,select排障用 - 规则从上往下命中即停,顺序按"从确定到模糊":内网 → 公司 → 明确域名 → 规则集 → GEOIP → MATCH
- TUN 模式覆盖所有程序(含
ping/dig/ssh),代价是需要权限且会与 VPN 客户端抢路由 - Clash Verge Rev 的 Merge/Script 增强层能让自定义规则在订阅更新后不丢
- 整网段分流用旁路由形态:设备网关指过去、开
ip_forward、规则复用同一份配置; 它是新的单点,只把需要的设备指过去 - 排障看两个字段:
rule(规则对不对)与chains(走了哪个节点)