很多人看到日志里出现 304,就以为规则没下载成功。HTTP 语义里,304 是正常的条件请求结果:客户端带着缓存标识去问服务端,服务端说内容没变。问题是,代理客户端的规则链路还多了本地缓存、provider 名称、behavior 类型和 reload 步骤,任何一处错位都会让新规则看起来没生效。

##判断 304 是否合理

curl 看响应头:

curl -I "https://example.com/rules/direct.yaml"

重点看:

  • ETag
  • Last-Modified
  • Cache-Control
  • Content-Length
  • CDN 或反向代理相关头

如果你刚刚改了上游文件,但响应头里的 ETag 和 Last-Modified 没变,问题可能在上游发布流程或 CDN 缓存。Mihomo 只是按服务端给出的信息复用旧文件。

找到本地缓存文件

rule-provider 通常会写 path

rule-providers:
  reject-list:
    type: http
    behavior: domain
    url: https://example.com/reject.yaml
    path: ./ruleset/reject.yaml
    interval: 86400

这个 path 才是本地实际加载的文件。打开它,确认里面有没有你期望的新规则。不要只看远端仓库页面,也不要只看订阅转换后的总配置。

如果 path 使用相对路径,要确认它相对于哪个工作目录。OpenClash、Docker、systemd service、桌面客户端的工作目录可能不同,同一个相对路径会落到不同位置。

provider 更新和配置 reload 是两步

下载 provider 文件,只代表文件落地。规则是否生效,还取决于核心是否重新读取它。不同客户端按钮文案不一样,有的叫更新 provider,有的叫重载配置,有的只在核心重启后重读。

排查时可以这样做:

  1. 手动触发 provider update。
  2. 打开本地 path,确认文件内容变化。
  3. 重载 Mihomo 配置。
  4. 用日志验证某个样本域名命中该 provider。

只看到「更新成功」不够。必须选一个样本规则,看它是否真的进入匹配链。

behavior 类型不能乱写

behavior 决定规则文件如何解释。常见值包括 domainipcidrclassical。如果上游文件是 classical 规则,你却写成 domain,加载可能失败或命中异常。

同样,provider 名称要和 rules 里的引用一致:

rules:
  - RULE-SET,reject-list,REJECT

名称写错时,文件可能正常更新,但根本没被规则链引用。日志里要看命中的规则集名,不要只看文件时间。

CDN 和 ETag 的坑

自建规则源常放在 GitHub raw、对象存储、CDN 或反向代理后面。它们的缓存策略不同:

位置常见现象处理
GitHub raw更新有短延迟等待或换 release 固定链接
CDNETag 未及时刷新purge 缓存或加版本参数
反向代理Last-Modified 被固定修代理头传递
对象存储Cache-Control 太长调短规则文件缓存时间

排查时不要频繁改 Mihomo 配置,用 curl -I 确认服务端头部,再决定是否清本地缓存。

使用配套订阅线路时,如果服务端已经内置规则提供器,自己再叠加同名 provider 前要改名,避免两个来源写到同一路径。

安全清缓存流程

可以删除本地 provider 文件,但建议先备份:

cp ruleset/reject.yaml ruleset/reject.yaml.bak
rm ruleset/reject.yaml

然后触发 provider update。若这次返回 200 并生成新文件,说明旧缓存链路确实影响了更新。若仍拿到旧内容,问题在远端缓存或 URL 指向,不在本机。

最后验收

选三条样本:一条应该命中该 provider 的域名,一条不应该命中的域名,一条 final 兜底。打开 debug 日志,看命中的规则集名和策略。只有日志里出现正确 provider 名称,才算更新生效。文件时间变新但命中链不变,仍然要继续查引用关系和 reload。

相关阅读