先区分整份订阅与节点 Provider

订阅更新失败,先看是谁在下载、下载的是什么。图形客户端的订阅更新和 mihomo 的 proxy-providers 是两条不同链路:前者通常导入整份配置,后者只刷新节点集合。节点能连接,不代表订阅地址此刻还能访问;订阅下载成功,也不代表里面的节点都可用。

  • 客户端订阅:可能包含 dns、proxy-groups、rules 等完整配置。更新频率由客户端管理,界面中的自动更新设置不等于内核 Provider 的定时器。
  • 节点 Provider:由 mihomo 在运行时读取。远程内容应采用它支持的节点集合格式,本文使用顶层 proxies: 的 YAML;策略组通过 use 引用它。
  • 健康检查:向测试地址发起请求,检查节点的可达性与延迟。它不负责重新下载订阅,也不能修复过期的订阅凭据。

按报错定位:先 HTTP,再解析,最后看节点

先保存一次失败更新对应的日志,记录时间、请求域名和错误末尾。不要只截取界面上的“更新失败”。日志若包含订阅查询参数,应先遮盖令牌再分享。下面这些报错属于不同阶段,不适合用同一种方法处理。

日志或状态 优先检查 下一步
no such host 订阅域名解析 运行 getent,核对域名是否拼错
i/o timeout、context deadline exceeded 连接路径、出口、超时阶段 分别测试直连与本地代理
x509: certificate has expired 本机时钟与服务端证书期限 先校时,再检查站点证书
certificate signed by unknown authority 系统 CA、企业 HTTPS 检查 核对可信根证书来源
HTTP 401 / 403 令牌、访问策略、User-Agent 核对服务商说明与响应正文
HTTP 429 请求频率限制 按 Retry-After 等待,拉长周期
yaml: unmarshal errors 响应格式、字段类型、缩进 检查返回内容是否为 HTML 或错误消息

超时不一定是节点故障

订阅域名可能只能通过特定网络访问,也可能在代理出口被限流。Linux 终端中的 HTTPS_PROXY 与桌面系统代理不一定一致,客户端更新器也未必使用相同设置。因此,“浏览器能打开网站”不能证明后台订阅请求走的是同一条路径。

以下命令先读取订阅地址,避免把令牌直接写入 shell 历史。响应存放在权限受限的临时目录。命令不自动跟随跳转,也不打印订阅正文;如果得到 301 或 302,应在本机检查跳转目标,确认来源后再访问。

umask 077
workdir=$(mktemp -d)
read -r -s -p '订阅 URL: ' SUB_URL
printf '\n'

curl --noproxy '*' --connect-timeout 10 --max-time 30 \
  -sS -D "$workdir/headers.txt" \
  -o "$workdir/body.txt" \
  -w 'HTTP=%{http_code} total=%{time_total}s\n' \
  "$SUB_URL"

getent ahosts example.com

最后一行中的 example.com 应换成订阅 URL 的主机名,只保留域名,不带协议和令牌。--noproxy '*' 用于排除 curl 的显式代理设置;如果系统开启了 TUN 或路由器透明代理,流量仍可能被接管,不能把这次结果当作物理直连的证明。

确认内核监听的 mixed-port 确实是 7890 后,可对比显式 HTTP 代理请求。下列命令继续使用上一步的变量,结果只表示这次请求的实际状态,不是预设的成功值。

curl --noproxy '' --proxy http://127.0.0.1:7890 \
  --connect-timeout 10 --max-time 30 \
  -sS -D "$workdir/proxy-headers.txt" \
  -o "$workdir/proxy-body.txt" \
  -w 'HTTP=%{http_code} total=%{time_total}s\n' \
  "$SUB_URL"

# 在本机完成检查后删除含订阅信息的临时文件
unset SUB_URL
rm -rf -- "$workdir"
unset workdir

证书错误、UA 被拒与格式不兼容

证书错误:先校时,不跳过验证

先执行 timedatectl status,确认日期、时区及时间同步状态。证书有效期依据绝对时间判断,时区显示不同本身不是证书过期的原因。时钟正确后,再检查系统 CA 包是否安装、企业网络是否替换了 HTTPS 证书,以及订阅服务的证书是否已过期。

Debian、Ubuntu 可检查 ca-certificates 软件包;若由管理员提供企业根证书,应按发行版流程安装并确认来源。不要把 curl -k 或跳过证书验证作为长期方案,订阅内容含连接凭据,验证失效会增加内容被替换的风险。

401、403:先检查凭据,再处理 User-Agent

401 常见于令牌失效;403 还可能来自套餐状态、IP 限制、User-Agent 策略或防护页面。应以服务商响应为准,不能见到 403 就反复换 UA。浏览器获得 HTTP 200 也可能只拿到登录页面,并非有效订阅。

如果服务商明确要求 mihomo 的 User-Agent,可以在 Provider 中设置请求头。header 的每个值使用字符串列表。以下是字段写法,放进对应的 HTTP Provider 内;它不能代替账户授权,也不应被用于绕过访问控制。

header:
  User-Agent:
    - mihomo

解析失败:检查正文结构,不只看文件扩展名

订阅地址以 .yaml 结尾,不代表服务器返回的就是 YAML。应在本机查看响应头和正文开头:HTML 防护页面、JSON 错误消息、Base64 节点列表、完整配置和 Provider 节点集合,是不同的数据形态。HTTP 200 仅表示请求层面成功。

本文使用的 Provider 正文应具有下面的顶层结构。示例不含真实节点,proxies: [] 只用于说明字段类型;实际服务必须提供有内容的节点列表。不要把整份配置直接当作节点 Provider,尤其不要期待其中的 rules 自动合并到主配置。

proxies: []
  • 主配置里的 Provider 名称必须唯一,建议使用稳定的英文名,例如 airport。
  • YAML 用空格缩进,不能用 Tab。URL 建议加引号,避免特殊字符引起歧义。
  • 节点格式转换应在可信环境完成,不要把带令牌的订阅提交给来源不明的转换站。

proxy-providers 定时刷新:完整字段与引用关系

以下配置用于说明连接关系:每隔 3600 秒拉取节点集合,每隔 600 秒进行健康检查。策略组 PROXY 通过 use: [airport] 引用节点;末尾规则将未匹配流量交给该组。它不是现成订阅,example.com 是文档示例域名,必须改成服务商提供的 Provider 地址后再使用。

mixed-port: 7890
allow-lan: false
mode: rule
log-level: info

proxy-providers:
  airport:
    type: http
    url: "https://example.com/subscription.yaml"
    path: ./providers/airport.yaml
    interval: 3600
    health-check:
      enable: true
      url: "https://www.gstatic.com/generate_204"
      interval: 600
      timeout: 5000
      lazy: true

proxy-groups:
  - name: PROXY
    type: select
    use:
      - airport

rules:
  - MATCH,PROXY
字段 示例值 作用与单位
interval 3600 远程 Provider 更新周期,单位为秒
path ./providers/airport.yaml 相对于内核工作目录的本地缓存路径
health-check.interval 600 节点健康检查周期,单位为秒
health-check.timeout 5000 单次检查超时,单位为毫秒
health-check.lazy true 未使用时可跳过周期检查,减少探测

health-check.url 应是可稳定返回预期响应的轻量地址。上面的 URL 只是常用示例,不保证在所有出口可达。测试地址故障会让许多正常节点同时显示异常,此时应先排除探测目标问题,而不是批量删除节点。

更新请求走哪个出口

当前 mihomo 的 HTTP Provider 可以通过 proxy 字段指定下载时使用的代理或策略组。未指定时,不要假定它一定沿用桌面系统代理。若订阅需要通过代理访问,应先确保指定出口能够在订阅首次下载前独立建立。

刷新周期不宜过短。3600 秒等于 1 小时,通常比每分钟轮询更合适;具体仍以服务商限制为准。健康检查每次可能涉及多个节点,节点越多,请求总量越大。单纯缩短检查间隔,不会提高订阅服务的更新速度。

把配置落盘:验证、权限与重启顺序

先确认当前运行方式。图形客户端生成的运行配置可能在下次切换订阅时覆盖手工修改,应使用客户端支持的持久化合并机制。独立运行 mihomo 时,则以实际启动参数中的 -d 和 -f 为准,不要修改一个没有被读取的同名文件。

  1. 备份现有配置,保留可以恢复的版本。
  2. 将 Provider 与策略组一起写入主配置,确认 use 与 Provider 名字完全一致。
  3. 建立缓存目录,确保运行内核的用户具有写权限。
  4. 运行配置检查;通过后才重启服务。
  5. 查看启动与后续更新日志,确认没有下载、解析或权限错误。

下面假定工作目录为 ~/.config/mihomo,配置文件为 config.yaml,并且已经创建名为 mihomo.service 的 systemd 用户服务。若使用系统级服务,配置目录和命令中的用户作用域应按实际部署调整。

cp ~/.config/mihomo/config.yaml \
  ~/.config/mihomo/config.yaml.bak
mkdir -p ~/.config/mihomo/providers
chmod 700 ~/.config/mihomo/providers
chmod 600 ~/.config/mihomo/config.yaml

mihomo -t -d ~/.config/mihomo \
  -f ~/.config/mihomo/config.yaml

# 仅在检查通过后执行
systemctl --user restart mihomo.service
journalctl --user -u mihomo.service -n 100 --no-pager

mihomo -t 用于配置检查,不能单独证明所有远程内容和节点都能工作。配置通过但更新仍失败时,要继续看实际运行日志。若服务报 permission denied,检查目录所有者、服务用户与缓存路径;不要直接给整个配置目录开放所有用户写权限。

Provider 缓存存在时,更新失败后可能仍能使用旧节点。界面显示“还有节点”并不等于刷新成功。可以结合日志和 stat ~/.config/mihomo/providers/airport.yaml 查看缓存状态;修改时间只是辅助线索,不能单独证明远程内容已变更。

常见追问:自动刷新为何看起来没有生效

配置了 interval,关闭客户端后还会更新吗?

不会由这段 YAML 自动唤醒已退出的内核。Provider 定时刷新依赖 mihomo 持续运行,计算机休眠、服务退出或网络断开都会影响执行。需要 Linux 常驻运行时,按部署方式配置 systemd;不要把一次成功启动等同于已完成开机自启。

订阅已更新,为什么规则和 DNS 没有变化?

proxy-providers 更新的是节点集合,不是整份主配置。规则来源可以另外使用 rule-providers,但规则集同样需要在 rules 中被引用。若希望替换整个配置,应使用客户端的订阅更新流程,并核对本地覆写规则的优先级。字段关系可继续查看配置参考。

健康检查成功,为什么特定网站仍打不开?

健康检查只验证测试目标,不覆盖所有网站。特定站点还可能受 DNS 解析、规则命中、出口区域或目标服务限制影响。先检查实际命中的策略组和节点,再对目标域名排查。不要因为一次探测返回正常,就认定整条访问链路没有问题。

怎样确认故障已经处理完?

  • 订阅请求返回有效内容,而不是登录页面或错误正文。
  • 运行日志没有 Provider 解析错误,策略组能显示预期节点。
  • 首次启动不依赖旧缓存,也能完成订阅拉取。
  • 等待一个配置周期后,再检查是否发生后续刷新。
  • 客户端重启后设置仍保留,令牌与节点凭据没有出现在公开日志中。

处理顺序保持固定:下载链路 → HTTP 状态 → 内容格式 → Provider 引用 → 定时更新 → 节点检查。这样能把“订阅拿不到”和“节点不能用”分开,避免反复重装客户端却没有触及故障点。首次部署可先按快速上手完成最小配置,再逐项加入自动刷新设置。