很多人看到日志里出现 304,就以为规则没下载成功。HTTP 语义里,304 是正常的条件请求结果:客户端带着缓存标识去问服务端,服务端说内容没变。问题是,代理客户端的规则链路还多了本地缓存、provider 名称、behavior 类型和 reload 步骤,任何一处错位都会让新规则看起来没生效。
##判断 304 是否合理
用 curl 看响应头:
curl -I "https://example.com/rules/direct.yaml"
重点看:
ETagLast-ModifiedCache-ControlContent-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,有的叫重载配置,有的只在核心重启后重读。
排查时可以这样做:
- 手动触发 provider update。
- 打开本地 path,确认文件内容变化。
- 重载 Mihomo 配置。
- 用日志验证某个样本域名命中该 provider。
只看到「更新成功」不够。必须选一个样本规则,看它是否真的进入匹配链。
behavior 类型不能乱写
behavior 决定规则文件如何解释。常见值包括 domain、ipcidr、classical。如果上游文件是 classical 规则,你却写成 domain,加载可能失败或命中异常。
同样,provider 名称要和 rules 里的引用一致:
rules:
- RULE-SET,reject-list,REJECT
名称写错时,文件可能正常更新,但根本没被规则链引用。日志里要看命中的规则集名,不要只看文件时间。
CDN 和 ETag 的坑
自建规则源常放在 GitHub raw、对象存储、CDN 或反向代理后面。它们的缓存策略不同:
| 位置 | 常见现象 | 处理 |
|---|---|---|
| GitHub raw | 更新有短延迟 | 等待或换 release 固定链接 |
| CDN | ETag 未及时刷新 | 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。