😤 一个几乎人人都遇到过的问题
你在配置文件里认认真真写下了这样的规则:
rules: - DOMAIN-SUFFIX,example.com,DIRECT - GEOIP,CN,DIRECT - MATCH,PROXY
你的意图很明确:example.com 走直连,国内 IP 走直连,其他走代理。
然后你打开浏览器访问 www.example.com,页面加载缓慢。你打开 ipinfo.io,显示的却是代理节点的 IP。你翻遍日志,发现这条规则根本没有被命中。
这不是个例。在 Clash 社区里,"我明明写了 DIRECT,为什么还是走了代理"是出现频率最高的提问之一。更让人困扰的是,它的原因不是单一的——可能来自规则语义、DNS 模式、连接状态、系统配置等十来个完全不同的层次。
本文的目标不是给出一个万能答案,而是提供一套系统化的排查框架,让你能逐层收敛,最终定位到具体的那一个根因。
🧠 先建立正确的心理模型
在深入具体原因之前,我们必须先修正一个常见的思维误区:"写了 DIRECT 规则,流量就一定直连"是不成立的。
DIRECT 规则的完整语义是:当这条规则被命中时,Clash 内核会将流量以直连方式转发出去。 注意其中的两个关键词:
- "被命中"——如果流量根本没有匹配到这条规则,DIRECT 就不会生效。
- "Clash 内核转发"——DIRECT 不是"绕过 Clash",而是"由 Clash 决定不经过代理节点转发"。流量依然经过内核处理。
因此,"直连失效"这个说法其实包含了两类完全不同的问题:
- 规则未被命中: 流量因为某种原因没有匹配到你的 DIRECT 规则。
- 规则被命中但行为不符: 规则命中了,但因为 DNS、IPv6 或连接复用的原因,流量的实际走向与预期不符。
这两类问题的排查方向完全不同。本文的十个原因将分别归属于这两类,并在诊断决策树中给出区分方法。
🔍 原因一:规则顺序被前置覆盖
这是最常见、也是最容易排查的原因。
问题表现
你的配置里明明写了 DOMAIN-SUFFIX,example.com,DIRECT,但在它的上方存在另一条更宽泛的规则把同样的流量提前拦截了:
rules: - DOMAIN-KEYWORD,example,PROXY # ← 这条在前面 - DOMAIN-SUFFIX,example.com,DIRECT # ← 你的规则,永远不会生效 - MATCH,PROXY
当访问 www.example.com 时,关键词规则会先命中,流量走了代理。你的 DIRECT 规则形同虚设。
为什么在大型规则集里特别常见?
当配置合并了多个 rule-provider 时,合并工具往往按"规则集加载顺序"拼接规则,而不会做语义层面的优先级分析。结果是:某个宽泛的 REJECT 或 PROXY 关键词规则可能恰好压在你的精确 DIRECT 规则之上。
关于规则集合并的工程化方法,可参考 《构建可维护的规则集》 中关于去重与顺序的部分。
诊断方法
开启 debug 日志,访问目标域名,观察日志中命中的是哪条规则:
[DEBUG] www.example.com matched rule: DOMAIN-KEYWORD,example,PROXY
如果命中的不是你的 DIRECT 规则,就说明问题在顺序上。
🎯 原因二:DOMAIN 与 DOMAIN-SUFFIX 的语义误用
这个原因的隐蔽性比顺序问题更高,因为它不涉及"顺序",而是规则本身写错了类型。
问题表现
rules: - DOMAIN,example.com,DIRECT - MATCH,PROXY
你期望 example.com 及其子域名都直连,但实际上 DOMAIN 只精确匹配 example.com 这一个域名。访问 www.example.com 时,规则不命中,最终落到 MATCH,PROXY,走了代理。
为什么容易混淆?
因为在日常表达中,"example.com"常常被当作"example.com 这个网站"的代称,包含了它的所有子域名。但规则的语义是严格的字符匹配,不是概念匹配。
完整的语义对比
| 请求域名 | DOMAIN,example.com | DOMAIN-SUFFIX,example.com |
|---|---|---|
example.com | ✅ 命中 | ✅ 命中 |
www.example.com | ❌ 不命中 | ✅ 命中 |
api.example.com | ❌ 不命中 | ✅ 命中 |
example.com.cn | ❌ 不命中 | ❌ 不命中 |
notexample.com | ❌ 不命中 | ❌ 不命中 |
详细的匹配引擎实现分析见 《DOMAIN 与 DOMAIN-SUFFIX 对决》。
诊断方法
把 DOMAIN 临时改为 DOMAIN-SUFFIX 测试。如果问题消失,说明就是类型误用。更严谨的方式是在 debug 日志中确认请求的完整域名,再与规则逐字符对比。
🌐 原因三:DNS 模式决定了匹配的对象
这是所有原因中最容易被忽略、也最"反直觉"的一个。
问题的本质
规则匹配的对象取决于 DNS 工作模式:
- fake-ip 模式: 匹配的是还原出的域名。你写的
DOMAIN-SUFFIX规则可以正常工作。 - redir-host 模式: 匹配的是DNS 解析出的真实 IP。域名类规则可能依然有效(内核会保留域名信息),但
GEOIP和IP-CIDR规则会以真实 IP 为准。
关键陷阱在于:如果你同时使用了域名规则和 IP 规则,并且 DNS 模式切换过,行为可能完全不同。
一个典型场景
你在 redir-host 模式下写了一条 GEOIP,CN,DIRECT,一切正常。后来为了优化延迟切换到 fake-ip 模式,发现某些国内网站开始走代理了——这是因为 fake-ip 模式下的 GEOIP 匹配时机和结果发生了变化,某些域名的真实 IP 可能被解析到海外 CDN。
与 TUN 模式的交互
在 TUN 模式下,DNS 劫持是默认行为,Fake-IP 映射表 成为规则匹配的关键基础设施。如果映射表出现问题(比如某个域名没有被正确记录),规则匹配就会失去依据。这部分细节在 TUN 模式 DNS 章节 中有完整讨论。
诊断方法
在客户端日志中查看 DNS 解析结果:
[DEBUG] dns resolve www.example.com -> 93.184.216.34 [DEBUG] matched rule: GEOIP,US,PROXY
如果解析出的 IP 属于海外,而你又依赖 GEOIP 规则做直连判断,就会产生"直连规则失效"的错觉。真正的解决方案是为该域名单独写一条基于域名的 DIRECT 规则。
🔁 原因四:连接复用延续了旧规则
这个原因非常微妙,通常发生在你"刚刚修改了规则"的场景中。
问题表现
你把某个域名从 PROXY 改成了 DIRECT,重启客户端后测试,发现流量还是走了代理。或者你更新了订阅,规则变了,但某些连接的行为没有变化。
为什么?
Clash 采用热重载机制——更新配置时不会强制断开已建立的连接,而是让它们继续使用旧配置运行,直到自然结束。这是为了让正在进行的下载、视频流不被打断,但副作用是旧规则的"残余"会持续一段时间。
更微妙的是,连接池复用 会让后续的请求继续使用已建立的连接——包括它们的出口节点和规则决策。这就是为什么"改了规则之后某些站点要过几分钟才生效"。
与规则匹配缓存的关系
除了连接复用,规则匹配缓存 也可能造成类似效果。虽然热重载会清空规则缓存,但在某些内核实现中,清空是异步的,可能存在短暂的窗口期。
诊断方法
查看当前活动连接列表(通过 API GET /connections),观察目标域名的连接是否仍然存在。如果存在,说明是旧连接在起作用,等待其超时或手动断开即可。
🔒 原因五:TUN 模式下的流量早于规则被劫持
TUN 模式是"直连失效"问题的高发区,因为它改变了流量进入 Clash 的方式。
问题的本质
在 TUN 模式下,所有流量(包括 UDP、ICMP)都被虚拟网卡拦截,无论目标地址是哪里。流量进入 Clash 后才会进行规则匹配。这意味着:
- 如果 TUN 的配置本身有问题(比如
auto-route或bypass设置不当),流量可能在规则匹配前就被错误路由。 - 如果规则里有
MATCH,PROXY兜底,而某个直连规则没命中,流量就会走代理——这本身没错,但用户会误以为"直连失败"。
最常见的情况:DNS 劫持的副作用
TUN 模式通常会启用 DNS 劫持,把 53 端口的查询重定向到 Clash 内置 DNS。如果这个过程中出现问题,某些域名的解析会失败或被缓存污染,导致后续的规则匹配基于错误的 IP。
诊断方法
检查 TUN 配置中的 bypass 列表是否正确包含了需要直连的网段。如果想把局域网和内网流量彻底绕过 Clash,应该在 TUN 层做 bypass,而不是仅靠规则。相关技术细节见 《拆分隧道完全指南》 和 《Windows TUN 驱动深度解析》。
🌍 原因六:IPv6 是规则的漏网之鱼
这个原因常常被完全忽略,因为大多数用户只关注 IPv4。
问题表现
你的规则里写了 IP-CIDR,10.0.0.0/8,DIRECT 和一系列 IPv4 直连规则,但访问某些支持 IPv6 的网站时,流量却走了代理。
为什么?
IPv4 和 IPv6 是两条独立的协议栈。当域名同时拥有 A 记录(IPv4)和 AAAA 记录(IPv6)时,操作系统会根据 RFC 6724 的地址选择策略决定使用哪一个。如果选择了 IPv6,而你的规则里只有 IPv4 的 IP-CIDR,那么这条流量可能:
- 无法命中 IPv4 规则。
- 直接落到
MATCH,PROXY,走了代理。 - 或者被
GEOIP规则错误判断(如果 GEOIP 数据库对 IPv6 的支持不完整)。
诊断方法
在 Windows 上用 ping -6 example.com 或 nslookup -type=AAAA example.com 检查是否有 IPv6 地址。在 Clash 日志中观察连接的目标地址类型。
解决方案
有两个方向:
- 补全 IPv6 规则: 添加
IP-CIDR6规则覆盖直连的 IPv6 网段。 - 禁用 IPv6: 在配置中关闭 IPv6 解析(
ipv6: false),强制所有流量走 IPv4。这样 IPv4 规则就能覆盖所有流量。
两种方案各有取舍。如果 ISP 的 IPv6 体验良好,建议补全规则;如果 IPv6 链路质量不稳定,禁用是更简单的选择。相关讨论见 《TCP 与 UDP 深度对比》 中的协议栈部分。
🧩 原因七:策略组嵌套导致出口偏离
这个原因的隐蔽性在于:规则确实命中了 DIRECT,但流量最终还是走了代理——因为它进入了另一个策略组,而这个策略组内部又选择了代理节点。
问题表现
proxy-groups:
- name: DIRECT
type: select
proxies:
- 日本节点 # ← 名字叫 DIRECT,但里面放的是代理节点!
- DIRECT
rules:
- DOMAIN-SUFFIX,example.com,DIRECT # 规则命中了
- MATCH,PROXY规则命中了名为 DIRECT 的策略组,但这个策略组是 select 类型,且当前选中的是"日本节点"。于是流量走了代理。
为什么会出现这种情况?
通常是因为用户对策略组的命名产生了误解——以为"策略组名叫 DIRECT,就代表直连"。实际上,策略组的名字只是一个标识符,它是否直连取决于类型和内部的选择。
另一个常见情形是策略组嵌套:
proxy-groups:
- name: 国内直连
type: select
proxies:
- 直连组 # ← 引用了另一个组
- name: 直连组
type: url-test
proxies: [节点A, 节点B] # ← 实际上是自动选节点"国内直连"这个组最终会走代理,因为它引用的"直连组"是一个 url-test 组。嵌套层的命名误导了用户。
诊断方法
在客户端 UI 中查看目标策略组的当前选中项。如果它显示的是一个节点而非 DIRECT,问题就在这里。策略组机制的完整解析见 《策略组完全解析》。
📦 原因八:rule-provider 静默加载失败
如果你依赖外部规则集做直连判断,加载失败会导致"规则存在但不生效"。
问题表现
你的规则里有 RULE-SET,cn-domain,DIRECT,但这个规则集在启动或更新时加载失败。Clash 不会因此报错退出,而是静默跳过这个规则,继续处理后续规则。结果是所有本应直连的域名都落到了 MATCH,PROXY。
为什么会静默失败?
因为 Clash 的设计哲学是"可用性优先"。如果某个 rule-provider 加载失败,与其让整个客户端无法工作,不如跳过这个规则集继续运行。但这也意味着用户完全无从察觉——除非主动检查日志。
诊断方法
在日志中搜索 rule-provider 或 provider 关键字:
[ERROR] rule-provider cn-domain: load failed: connect timeout [WARN] rule-provider cn-domain: using cached version (expired)
如果看到类似日志,说明规则集没有按预期更新或加载。
解决方案
- 检查 provider URL 是否可访问。
- 延长
interval,避免启动时因网络问题触发失败。 - 改用本地文件路径的 provider,彻底避免网络依赖。
- 使用 GitHub Actions 定期拉取到自己的仓库,客户端从 CDN 读取。方案见 规则集自动化更新。
🔬 原因九:DNS 解析结果与预期 IP 不符
这个原因与第三个原因(DNS 模式)相关,但更偏向"解析结果异常"而非"模式差异"。
问题表现
你期望某个域名解析到国内 IP,从而命中 GEOIP,CN,DIRECT 规则。但实际解析到了一个海外 IP,导致流量走了代理。
为什么会这样?
- CDN 调度: 大型网站使用 Anycast 或 GeoDNS,从不同位置解析可能返回不同 IP。
- DNS 污染: 本地网络中的 DNS 请求被篡改,返回了错误的 IP。
- 上游 DNS 选择: Clash 配置中的
nameserver与fallback策略可能导致国内外域名被送到不同的解析器。 - 缓存污染: 曾经解析到错误 IP 的结果被缓存,后续查询继续使用错误结果。
诊断方法
在 debug 日志中对比不同 DNS 服务器的解析结果:
[DEBUG] dns: query www.example.com via 223.5.5.5 -> 93.184.216.34 (US) [DEBUG] dns: query www.example.com via 8.8.8.8 -> 104.18.32.7 (US)
如果国内 DNS 也返回了海外 IP,说明该域名确实使用了海外 CDN,此时应该用域名规则而非 GEOIP 规则。
解决方案
对于需要精确直连的关键域名,不要依赖 GEOIP,而是直接使用 DOMAIN-SUFFIX 规则。这是最可靠的方式。DNS 缓存的完整机制见 缓存系统的 DNS 章节。
🖥️ 原因十:多客户端与系统代理叠加
最后这个原因与 Clash 本身无关,但会造成"规则不生效"的假象。
问题表现
你同时运行了 Clash 和另一个代理工具(比如旧版 CFW、Clash Verge、Surge、或者其他 VPN 客户端),或者系统中残留了旧的代理设置。流量被另一个程序接管,Clash 的规则自然无从生效。
为什么难以察觉?
因为多个代理工具会争抢系统代理设置。后启动的程序会覆盖先启动的设置,你看到的现象是"规则时灵时不灵"——实际上取决于哪个程序先启动、哪个程序后覆盖。
诊断方法
检查系统中是否有多个代理进程:
tasklist | findstr /i "clash" netstat -ano | findstr "7890" route print | findstr "0.0.0.0"
如果发现多个代理相关进程或多个默认路由,说明存在叠加。
与 TUN 驱动残留的关系
从 CFW 迁移到其他客户端的用户,经常会遇到旧的 cfw-tun 虚拟网卡残留问题。这会导致路由表混乱,流量可能被错误地引导到已经失效的接口。彻底的清理方法见 《Windows TUN 驱动深度解析》 的清理章节。
🌳 系统化诊断决策树
把十个原因组织成可执行的排查顺序,能显著提升效率。以下是推荐的诊断流程:
目标:定位"直连规则失效"的根因
步骤 1:确认问题是"规则未命中"还是"命中后行为不符"
├─ 打开 debug 日志,访问目标域名
├─ 观察日志中命中的规则
│
├─ [未命中你的 DIRECT 规则]
│ ├─ 检查是否有更靠前的规则覆盖 → 原因一
│ ├─ 检查规则类型是否为 DOMAIN(应用 SUFFIX)→ 原因二
│ ├─ 检查 rule-provider 是否加载失败 → 原因八
│ └─ 检查 IPv6 是否绕过了 IPv4 规则 → 原因六
│
└─ [命中了 DIRECT 规则,但流量仍走代理]
├─ 检查策略组当前选中项是否为节点 → 原因七
├─ 检查是否有旧连接仍在复用 → 原因四
├─ 检查 TUN 配置的 bypass 列表 → 原因五
└─ 检查 DNS 解析结果是否符合预期 → 原因三、九
步骤 2:确认问题是否与"时机"有关
├─ 刚改了规则不生效 → 原因四(连接复用/缓存)
├─ 重启后正常 → 确认是热重载滞后
└─ 一直不生效 → 回到步骤 1
步骤 3:确认是否存在环境干扰
├─ 检查是否有多个代理进程 → 原因十
├─ 检查虚拟网卡是否有残留 → 原因十
└─ 检查系统代理设置是否被覆盖 → 原因十
按这个顺序排查,通常能在 5-10 分钟内定位到具体原因。每一步都有明确的验证方法,不需要凭直觉猜测。
🛡️ 预防性设计:让直连规则更可靠
与其在出问题后排查,不如在设计配置时就避免这些陷阱。
设计原则一:规则顺序遵循"精确到宽泛"
rules: # 1. 精确 REJECT(尽早丢弃) - DOMAIN-SUFFIX,ads.example.com,REJECT # 2. 精确 DIRECT - DOMAIN-SUFFIX,intranet.company.com,DIRECT # 3. 精确 PROXY - DOMAIN-SUFFIX,blocked-site.com,PROXY # 4. 宽泛规则集 - RULE-SET,reject-list,REJECT - RULE-SET,proxy-list,PROXY # 5. GEOIP 兜底 - GEOIP,CN,DIRECT # 6. MATCH 永远最后 - MATCH,PROXY
设计原则二:关键域名用 SUFFIX 而非 DOMAIN
除非你真的只想匹配一个确切的域名(极少数情况),否则默认使用 DOMAIN-SUFFIX。这样能覆盖所有子域名,避免"部分生效"的困惑。
设计原则三:策略组命名避免语义误导
不要给一个实际会走代理的策略组起名叫"直连"。建议用用途而非行为命名:国内网站、海外视频、下载专用。这样即使内部配置变化,名字依然清晰。
设计原则四:显式处理 IPv6
如果你的网络环境支持 IPv6,建议在配置中做出明确选择:
- 如果要代理 IPv6,配置
ipv6: true并补充 IPv6 规则。 - 如果不打算处理,配置
ipv6: false禁用 IPv6 解析,让所有流量走 IPv4。
不要留空——留空意味着行为取决于系统和网络环境,可能导致不可预测的结果。
设计原则五:为规则集加载失败做兜底
如果依赖 rule-provider 做直连,建议在规则集中同时保留关键的本地规则。这样即使 provider 加载失败,核心的直连需求仍然能满足。例如:
rules: - DOMAIN-SUFFIX,important-cn-site.com,DIRECT # 本地硬编码 - RULE-SET,cn-domain,DIRECT # provider 兜底 - MATCH,PROXY
设计原则六:定期审计规则
规则会随着时间累积,也可能随着服务变化而失效。定期的规则审计能发现"死规则"和"冲突规则"。审计方法见 《规则膨胀的代价》 中的审计章节。
📌 总结:直连失效是一道减法题
回顾本文的十个原因,你会发现它们并不是孤立的技巧,而是一道逐层排除的减法题:
| # | 原因 | 归属 | 验证成本 |
|---|---|---|---|
| 1 | 规则顺序被覆盖 | 未命中 | 低(看日志) |
| 2 | DOMAIN 语义误用 | 未命中 | 低(改类型测试) |
| 3 | DNS 模式差异 | 行为不符 | 中(需理解两种模式) |
| 4 | 连接复用滞后 | 行为不符 | 低(重启验证) |
| 5 | TUN 模式劫持 | 行为不符 | 中(需理解 TUN 层) |
| 6 | IPv6 漏网 | 未命中 | 中(需协议栈知识) |
| 7 | 策略组嵌套 | 行为不符 | 低(看 UI 选中项) |
| 8 | Provider 加载失败 | 未命中 | 低(看日志) |
| 9 | DNS 解析异常 | 未命中 | 中(需对比解析) |
| 10 | 多客户端叠加 | 环境干扰 | 低(看进程列表) |
核心结论可以浓缩为三句话:
- DIRECT 生效的前提是规则被命中——顺序、类型、Provider 加载都可能让规则失效。
- 规则命中不等于流量直连——策略组嵌套、连接复用、TUN 劫持都可能改变实际走向。
- 环境永远在参与决策——DNS 模式、IPv6、多客户端叠加都会影响最终结果。
理解了这三点,你就不会再被"明明写了直连规则"所困扰。因为你知道,问题的答案从来不在那一行规则本身,而在它所处的整个系统之中。
如果你想继续深化这条排查路径,建议按顺序阅读:
- 《DOMAIN 与 DOMAIN-SUFFIX 对决》 —— 规则语义的底层原理
- 《Traffic Lifecycle in Clash》 —— 规则匹配在生命周期中的位置
- 《缓存系统全解析》 —— 连接复用与规则缓存的影响
- 《拆分隧道完全指南》 —— TUN 层 bypass 与规则层 DIRECT 的区别
- 《规则膨胀的代价》 —— 规则数量带来的隐性成本
- 《构建可维护的规则集》 —— 用工程化方法避免规则冲突