Mihomo 操作手册

7 分钟
本页目录

按实际连接追踪:流量是否进入 Mihomo、带着什么域名/IP、命中哪条规则、最终经过哪个代理。配置文件、运行配置和现有连接可能属于三个不同时间点。

当前任务入口
改配置没效果验证与运行状态
规则走错代理首条匹配与连接证据
旁路由整网异常入口、DNS、路由

先确定实际配置路径与 controller

配置文件要与运行实例对应

# 查 ExecStart、工作目录与覆盖文件
systemctl cat mihomo.service

确认二进制与 -d/-f 路径。容器或 OpenWrt 插件部署应查其管理入口,不能同时用本例 systemd 修改另一份配置。

# 确认服务主进程和最近启动结果
systemctl status mihomo.service --no-pager

需要完整日志另用 journalctl -u mihomo.service -b -n 100 --no-pager;配置文件存在不代表此进程正在使用它。

# 确认实际部署核心的版本
mihomo -v

应使用 ExecStart 指向的同一二进制;同名 PATH 命令可能是另一版本。

# 校验准备发布的配置文件
mihomo -t -f /opt/mihomo/mihomo_config/config.yaml

路径与工作目录按真实部署调整,尤其相对 rule-provider 路径。失败先修复;通过后还需运行 API、订阅与业务验证。

路径与 127.0.0.1:9999 是本手册保留的部署示例,不是 Mihomo 默认值。以 ExecStart 的 -d/-f、external-controller 和 secret 为准;API 不应无鉴权暴露到公网。配置检查不证明订阅、DNS 或代理目标实时可用。

在客户端加载认证后再读 API

以下在能够访问 controller 的客户端执行,示例 controller 位于本机 9999。通过当前 shell 的受控密钥加载方式设置 MIHOMO_SECRET;也可在终端交互读取,不将它写入历史。每个请求检查该变量非空。

# 在当前终端隐藏输入 controller secret
read -r -s MIHOMO_SECRET

输入后按 Enter;这是设置变量的一种方式,使用密钥管理器时不重复执行。结束本轮操作后可 unset MIHOMO_SECRET 清除当前 shell 变量。

# 确认 controller 身份与 API 连通性
curl -fsS -H "Authorization: Bearer ${MIHOMO_SECRET:?Load the controller secret in this shell}" http://127.0.0.1:9999/version

401 先核对 secret,连接拒绝查监听地址/端口;若状态码需要详细检查,临时加 -i 展示响应头。

# 只读运行模式与实际入口配置
curl -fsS -H "Authorization: Bearer ${MIHOMO_SECRET:?Load the controller secret in this shell}" http://127.0.0.1:9999/configs | jq '{mode,port,"mixed-port": .["mixed-port"],tun}' 

需要 jq;重点比较 mode、mixed-port 与 tun 是否符合预期。文件内容与运行值不同,先确认读取的是同一 controller。

重载前备份源文件,确认新配置在进程允许的工作目录/SAFE_PATHS 中。运行时 PATCH 不等于把变更保存回源文件;服务重启后按实际持久配置重新检查。文档中的 force 参数不表示“忽略无效配置”。

重载源文件与临时 PATCH 分开

先保存可回退的旧文件,并用上节 -t 校验新文件。下面的 path 属于 Mihomo 进程所在机器,不是 API 客户端的路径;需满足工作目录 / SAFE_PATHS 约束。

# 让运行实例重新加载已验证的文件
curl -fsS -i -X PUT "http://127.0.0.1:9999/configs?force=true" \
  -H "Authorization: Bearer ${MIHOMO_SECRET:?Load the controller secret in this shell}" \
  -H 'Content-Type: application/json' \
  --data '{"path":"/opt/mihomo/mihomo_config/config.yaml"}' 

核对 HTTP 状态,再重复 GET configs / rules 与新建连接验证。失败恢复旧文件后按同一流程校验、重载;不靠 force 忽略错误。

# 仅在需要临时调整时切换运行 mode
curl -fsS -i -X PATCH http://127.0.0.1:9999/configs \
  -H "Authorization: Bearer ${MIHOMO_SECRET:?Load the controller secret in this shell}" \
  -H 'Content-Type: application/json' \
  --data '{"mode":"rule"}' 

这是独立选择,不是重载后的必做步骤。先记录旧 mode,GET 核对新值;需要长期保存应更新受管理源配置,回退按记录值 PATCH。

HTTP 204 表示请求完成;再 GET configs、查看规则和新连接。重载可能重建监听/隧道,不能承诺已有连接全部无感。

先匹配到的规则决定去向

正在呈现知识画面
例外规则放在通配规则前还是后

规则自上而下首条匹配。api.example.com 同时满足精确域名和域名后缀,把 DIRECT 后缀放前面会遮住 REJECT 例外。

用运行规则与连接互相核对

# 查看运行实例加载的规则及顺序
curl -fsS -H "Authorization: Bearer ${MIHOMO_SECRET:?Load the controller secret in this shell}" http://127.0.0.1:9999/rules | jq '.rules'

比对规则类型、payload、策略目标,首条匹配优先。文件更新但这里未更新时先查重载。

# 找到本次请求的连接并读实际命中链
curl -fsS -H "Authorization: Bearer ${MIHOMO_SECRET:?Load the controller secret in this shell}" http://127.0.0.1:9999/connections |
  jq '.connections[] | {id,source: .metadata.sourceIP,host: .metadata.host,destination: .metadata.destinationIP,port: .metadata.destinationPort,rule,rulePayload,chains}' 

按源 IP、目标、端口与时间关联;id 用于后续精确操作。短连接可能已消失,可保持请求或结合日志重现。

# 读取策略组当前选择与可选节点
curl -fsS -H "Authorization: Bearer ${MIHOMO_SECRET:?Load the controller secret in this shell}" http://127.0.0.1:9999/proxies | jq '.proxies'

组名 PROXY、节点名 NODE 必须从实际返回取得;名称含空格/非 ASCII 时对 URL 路径做编码,JSON 值也需正确转义。

选择节点只针对可手动选择的组

# 将已核对的 Selector 组切到指定成员
curl -fsS -i -X PUT http://127.0.0.1:9999/proxies/PROXY \
  -H "Authorization: Bearer ${MIHOMO_SECRET:?Load the controller secret in this shell}" \
  -H 'Content-Type: application/json' \
  --data '{"name":"NODE"}' 

先记录旧 now;成功后 GET proxies 核对,再建立新连接看 chains。自动选择组不应按 Selector 操作;持久化取决于 profile.store-selected 等实际配置。

# 仅断开刚核对的那一条连接
curl -fsS -i -X DELETE -H "Authorization: Bearer ${MIHOMO_SECRET:?Load the controller secret in this shell}" http://127.0.0.1:9999/connections/CONNECTION_ID

CONNECTION_ID 从 connections 取得;该请求会中断这条业务连接。再次查询确认消失,再由应用重连;不省略 ID 误断全部连接。

区分“规则选到策略组”和“组最终选到节点”;检查 chains 与 now,而非只看配置文本。域名规则需要可用域名信息,IP 流量可能靠 DNS 映射或嗅探关联;redir-host 不等于一定只能靠嗅探。fake-ip 是映射标识,不是目标服务器真实 IP,直接拿它测公网路由会误判。

只验证新建连接,因为已有连接可能保留旧选择。确需断开时先读取特定 id,再 DELETE /connections/ID;DELETE /connections 会断开全部连接,不当作日常“刷新按钮”。

显式代理与透明入口分开验证

先验证显式入口

# 从入口所在主机发起一个显式代理请求
curl -v --connect-timeout 5 --proxy http://127.0.0.1:7890 https://example.com/

7890 按 GET configs 实际端口替换;核对连接记录的规则与 chains。远程客户端 localhost 指自己,不能指向另一台设备的代理。

# 查看透明流量选择路由表的策略
ip rule show

在实际透明网关执行,结合来源和 mark 判断规则;表内容另用 ip route show table all。

# 核对代理、DNS 与 controller 的实际监听
ss -lntup

与配置端口逐项对应;监听在回环只供本机访问。

# 只读透明截获规则及其管理者
sudo nft -a list ruleset

关注实际入口接口、端口、mark 与路由表联动;插件生成规则要回到插件配置修改,不能把整个 ruleset 清空。

现象下一步
显式代理成功,LAN 客户端失败查客户端网关/DNS、透明截获与路由,而非先换所有节点
请求不在 connections 中确认端口、进程和入口;瞬时连接可能已结束,结合日志抓复现
规则正确但目标失败查节点可达性、协议、DNS、证书和目标服务,不把 delay 测试当全站可用

Nikki/TUN/TProxy 的规则与端口按实际版本配置查看,不固定写死为某个 7891。旁路由自定义 fail-open 只有部署了对应机制才存在,而且直连可能违背路由策略;不要把停进程后“应该自动恢复直连”当默认事实。恢复后同时验证 DNS、TCP、UDP 和局域网例外。

Mihomo API · Routing rules · DNS · TUN