明明写了直连规则,为什么流量仍然进入代理?

十个可定位的技术真相,帮你撕开"玄学"的表象

📅 2026-09-29 🕒 阅读时间约 24 分钟 🏷️ 排障深潜
📑 本文目录

😤 一个几乎人人都遇到过的问题

你在配置文件里认认真真写下了这样的规则:

rules:
  - DOMAIN-SUFFIX,example.com,DIRECT
  - GEOIP,CN,DIRECT
  - MATCH,PROXY

你的意图很明确:example.com 走直连,国内 IP 走直连,其他走代理。

然后你打开浏览器访问 www.example.com,页面加载缓慢。你打开 ipinfo.io,显示的却是代理节点的 IP。你翻遍日志,发现这条规则根本没有被命中。

这不是个例。在 Clash 社区里,"我明明写了 DIRECT,为什么还是走了代理"是出现频率最高的提问之一。更让人困扰的是,它的原因不是单一的——可能来自规则语义、DNS 模式、连接状态、系统配置等十来个完全不同的层次。

本文的目标不是给出一个万能答案,而是提供一套系统化的排查框架,让你能逐层收敛,最终定位到具体的那一个根因。

💡 阅读建议: 如果你还没建立规则系统的基础认知,建议先阅读 《DOMAIN 与 DOMAIN-SUFFIX 对决》 和 《如何编写高效的自定义代理规则》,再回来看本文的深层原因分析。

🧠 先建立正确的心理模型

在深入具体原因之前,我们必须先修正一个常见的思维误区:"写了 DIRECT 规则,流量就一定直连"是不成立的。

DIRECT 规则的完整语义是:当这条规则被命中时,Clash 内核会将流量以直连方式转发出去。 注意其中的两个关键词:

  • "被命中"——如果流量根本没有匹配到这条规则,DIRECT 就不会生效。
  • "Clash 内核转发"——DIRECT 不是"绕过 Clash",而是"由 Clash 决定不经过代理节点转发"。流量依然经过内核处理。

因此,"直连失效"这个说法其实包含了两类完全不同的问题:

  1. 规则未被命中: 流量因为某种原因没有匹配到你的 DIRECT 规则。
  2. 规则被命中但行为不符: 规则命中了,但因为 DNS、IPv6 或连接复用的原因,流量的实际走向与预期不符。

这两类问题的排查方向完全不同。本文的十个原因将分别归属于这两类,并在诊断决策树中给出区分方法。

💡 与流量生命周期的关系: 规则匹配发生在流量生命周期的第三阶段。理解这一点有助于你判断"问题发生在规则的哪一侧"。如果你还没读过,建议先看 《Traffic Lifecycle in Clash》,建立完整的阶段认知。

🔍 原因一:规则顺序被前置覆盖

这是最常见、也是最容易排查的原因。

问题表现

你的配置里明明写了 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 规则,就说明问题在顺序上。

⚠️ 修复原则: 精确规则永远在宽泛规则之前。REJECT 通常最靠前(尽早丢弃),然后是精确的 DIRECT 和 PROXY,最后是 GEOIP 和 MATCH 兜底。

🎯 原因二: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.comDOMAIN-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),观察目标域名的连接是否仍然存在。如果存在,说明是旧连接在起作用,等待其超时或手动断开即可。

💡 验证技巧: 完全退出 Clash 再重新启动,如果问题消失,基本可以确认是连接复用造成的滞后。这与 订阅更新流程 中讨论的热重载机制直接相关。

🔒 原因五: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 驱动深度解析》。

⚠️ 关键区别: 规则里的 DIRECT 是"经过 Clash 但不走节点",TUN 的 bypass 是"根本不进入 Clash"。两者解决的是不同层次的问题,不能互相替代。

🌍 原因六: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规则顺序被覆盖未命中低(看日志)
2DOMAIN 语义误用未命中低(改类型测试)
3DNS 模式差异行为不符中(需理解两种模式)
4连接复用滞后行为不符低(重启验证)
5TUN 模式劫持行为不符中(需理解 TUN 层)
6IPv6 漏网未命中中(需协议栈知识)
7策略组嵌套行为不符低(看 UI 选中项)
8Provider 加载失败未命中低(看日志)
9DNS 解析异常未命中中(需对比解析)
10多客户端叠加环境干扰低(看进程列表)

核心结论可以浓缩为三句话:

  1. DIRECT 生效的前提是规则被命中——顺序、类型、Provider 加载都可能让规则失效。
  2. 规则命中不等于流量直连——策略组嵌套、连接复用、TUN 劫持都可能改变实际走向。
  3. 环境永远在参与决策——DNS 模式、IPv6、多客户端叠加都会影响最终结果。

理解了这三点,你就不会再被"明明写了直连规则"所困扰。因为你知道,问题的答案从来不在那一行规则本身,而在它所处的整个系统之中。

如果你想继续深化这条排查路径,建议按顺序阅读:

📚 继续探索