先判断故障发生在哪一层
浏览器可以打开网页,而终端中的 curl、git、包管理器或开发工具连接失败,通常不表示 Clash 内核已经失效。更常见的原因是两类程序采用了不同的代理入口:浏览器可能读取操作系统代理,也可能由扩展单独接管;终端程序则往往只读取自己的配置、代理环境变量或命令行参数。
排查时应把路径拆成四层:客户端界面是否正常管理内核、内核是否监听本机端口、目标程序是否把请求送到该端口、DNS 与规则是否在送入内核后正确处理。每次只验证一层,才能确定故障边界。
| 现象 | 优先检查 | 常见原因 |
|---|---|---|
| 浏览器正常,curl 失败 | 终端代理环境变量 | curl 没有读取系统代理,或变量指向旧端口 |
| 浏览器和终端都失败 | 内核状态与监听端口 | 内核未启动、配置加载失败或端口被占用 |
| 域名失败,IP 请求正常 | DNS 路径 | 本机解析失败、应用绕过 Clash DNS 或 fake-ip 不兼容 |
| 只有 Git 或包管理器失败 | 应用专用配置 | 应用覆盖了环境变量,或仍保存旧代理地址 |
| 开启 TUN 后终端恢复 | 原系统代理路径 | 程序原先不读取系统代理,TUN 改为从网络层接管 |
第一步:确认内核启动并监听正确端口
从客户端界面读取实际端口
不要先假定端口一定是 7890。许多 Clash 或 mihomo 配置会使用 mixed-port: 7890,但也可能分别设置 port: 7890 与 socks-port: 7891,客户端还可能因端口冲突改用其他数值。应在当前客户端的「设置」→「参数设置」或「设置」→「端口设置」中查看 HTTP、SOCKS5、Mixed Port 的实际值;菜单名称会随客户端版本变化,以界面显示为准。
配置文件中的典型监听设置如下。这里只用于辨认字段,实际排查必须以当前已加载的配置和运行状态为准。
mixed-port: 7890
allow-lan: false
mode: rule
log-level: info
mixed-port 可以在同一端口接受 HTTP 与 SOCKS5 连接。若配置使用分离端口,则 HTTP 客户端应连接 port,SOCKS5 客户端应连接 socks-port。协议与端口配错时,常见结果是连接立即断开、返回空响应或提示代理握手失败。
在本机检查监听状态
Windows PowerShell 可以检查指定端口是否处于监听状态:
Get-NetTCPConnection -State Listen |
Where-Object LocalPort -In 7890,7891 |
Select-Object LocalAddress,LocalPort,OwningProcess
macOS 与 Linux 可以使用:
lsof -nP -iTCP:7890 -sTCP:LISTEN
lsof -nP -iTCP:7891 -sTCP:LISTEN
如果系统提供 ss,也可以执行:
ss -lntp | grep -E ':(7890|7891)\b'
看到 127.0.0.1:7890 表示端口只接受本机连接,终端与 Clash 在同一台机器上时足够使用。若命令运行在 WSL、容器、虚拟机或另一台局域网设备中,那个环境里的 127.0.0.1 指向它自己,而不是宿主机。此时需要明确宿主机地址、虚拟网络边界以及 allow-lan 设置,不应直接把监听地址改为全网可达后停止检查。
第二步:绕过系统设置,直接测试代理端口
确认端口监听后,用显式参数让请求直接进入代理。这样可以把“内核与节点是否可用”和“操作系统有没有正确分发代理设置”分开。
使用 curl 验证 HTTP 代理
curl -v --connect-timeout 10 \
-x http://127.0.0.1:7890 \
https://example.com/
Windows PowerShell 中如果 curl 是 Invoke-WebRequest 的别名,应明确调用 curl.exe:
curl.exe -v --connect-timeout 10 ^
-x http://127.0.0.1:7890 ^
https://example.com/
若显式代理请求成功,而不带 -x 的请求失败,说明内核、监听端口和当前策略基本可用,问题集中在终端程序没有使用代理。若显式请求也失败,应同时观察 Clash 日志:日志中完全没有连接记录,通常表示请求没有到达该端口;出现连接记录但策略命中 REJECT、节点超时或 TLS 错误,则要继续检查规则、策略组和上游连接。
验证 SOCKS5,并区分本地解析与代理解析
curl -v --connect-timeout 10 \
--proxy socks5h://127.0.0.1:7891 \
https://example.com/
socks5h 中的 h 表示让代理侧处理域名,而 socks5 往往先在本地解析域名。若 socks5h 成功、socks5 失败,故障重点就从代理端口转向本机 DNS。这个对照测试比直接更换节点更能说明问题。
测试目标应选择稳定且符合当前规则预期的地址。若规则把某个域名设为 DIRECT,该请求可能不会经过所选代理节点;若规则设为 REJECT,失败就是配置决定的结果。规则模式按顺序求值,第一条匹配规则决定去向,因此还要在日志中核对实际命中的规则与策略。
第三步:为终端设置正确的代理变量
macOS 与 Linux 当前 Shell
对于使用 HTTP 或 Mixed Port 的本机 Clash,可以在当前终端会话中设置:
export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
export ALL_PROXY=socks5h://127.0.0.1:7891
export NO_PROXY=localhost,127.0.0.1,::1
不少程序只识别小写变量,也有程序优先读取大写变量。为了排除兼容差异,可以成对设置,但必须保证值一致:
export http_proxy="$HTTP_PROXY"
export https_proxy="$HTTPS_PROXY"
export all_proxy="$ALL_PROXY"
export no_proxy="$NO_PROXY"
这些命令只影响当前 Shell 及其之后启动的子进程,不会自动修改已经打开的编辑器、终端标签页或后台服务。需要持久化时,再按使用的 Shell 写入 ~/.zshrc、~/.bashrc 或对应启动文件,并在修改后新开终端验证。不要同时在多个启动文件中保留不同端口。
Windows PowerShell 当前会话
$env:HTTP_PROXY = "http://127.0.0.1:7890"
$env:HTTPS_PROXY = "http://127.0.0.1:7890"
$env:ALL_PROXY = "socks5://127.0.0.1:7891"
$env:NO_PROXY = "localhost,127.0.0.1,::1"
使用 $env: 设置的变量只在当前 PowerShell 进程及其子进程中有效。通过系统设置写入用户环境变量后,已经运行的终端也不会自动刷新,需要关闭并重新打开。检查实际值时执行:
Get-ChildItem Env: |
Where-Object Name -Match '^(HTTP|HTTPS|ALL|NO)_PROXY$'
清理旧端口与错误变量
客户端切换过端口后,终端里可能仍保存旧值。例如 Clash 已改为 7897,而 HTTPS_PROXY 还指向 7890。macOS 与 Linux 可使用以下命令临时清除:
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
unset http_proxy https_proxy all_proxy no_proxy
PowerShell 当前会话可使用:
Remove-Item Env:HTTP_PROXY -ErrorAction SilentlyContinue
Remove-Item Env:HTTPS_PROXY -ErrorAction SilentlyContinue
Remove-Item Env:ALL_PROXY -ErrorAction SilentlyContinue
Remove-Item Env:NO_PROXY -ErrorAction SilentlyContinue
清除后先执行一次不带代理的请求,再按实际端口重新设置。这样能确认问题不是多个来源相互覆盖。
第四步:检查 Git、包管理器与开发工具的独立配置
环境变量正确并不代表每个工具都会采用它。Git、npm、Python 工具链、Java 构建工具、编辑器和容器运行时都可能保存自己的代理配置。排查顺序应是先查看来源,再决定删除还是修改,而不是同时写入所有配置层。
Git 的全局与仓库配置
git config --show-origin --get-regexp 'http\..*proxy|https\..*proxy'
--show-origin 会显示配置来自系统、用户还是当前仓库。若发现旧端口,可以更新全局配置:
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890
如果希望 Git 改为跟随环境变量,则删除它自己的覆盖项:
git config --global --unset http.proxy
git config --global --unset https.proxy
还要检查仓库级配置,因为 .git/config 中的设置可能覆盖用户配置。HTTPS 请求通过 HTTP 代理时,https.proxy 的值通常仍以 http:// 开头,表示客户端先连接 HTTP 代理,再使用 CONNECT 建立 TLS 隧道;它不表示最终网站使用明文 HTTP。
npm、pnpm 与 Python 工具
npm config get proxy
npm config get https-proxy
pnpm config get proxy
pnpm config get https-proxy
python -m pip config list -v
输出为旧地址时,应在对应工具的用户配置中修正。npm 可以通过以下命令删除覆盖项,让它重新读取环境变量:
npm config delete proxy
npm config delete https-proxy
Python 的 pip 既可能读取环境变量,也可能读取用户目录或虚拟环境内的配置。使用 python -m pip 而不是单独输入 pip,可以确认正在检查的是当前 Python 解释器对应的工具。
编辑器内置终端与远程开发
从桌面图标启动的编辑器,可能在终端代理变量修改之前就已经运行。其内置终端继承的是编辑器进程启动时的环境,因此需要完全退出编辑器后重新打开。远程 SSH、开发容器与 WSL 又是独立环境:宿主机的系统代理不会自动变成远程主机的代理,宿主机的 127.0.0.1:7890 对远程主机也不可达。
第五步:定位 DNS、规则模式与 TUN 差异
用域名和 IP 对照 DNS 路径
终端报错包含 Could not resolve host、Name or service not known 或 getaddrinfo failed 时,连接甚至还没有进入目标站点。先执行系统解析测试:
nslookup example.com
curl -v https://example.com/
再对照使用 socks5h 的请求。如果代理侧解析成功而普通请求失败,应检查操作系统 DNS、VPN 或网络扩展的解析路径,以及应用是否自行指定 DNS。mihomo 的 fake-ip 模式会返回保留地址并由内核映射域名,这要求相关流量确实回到内核;某些跳过系统网络栈、固定使用自定义 DNS 的程序可能表现不同。
no-resolve 只作用于带有该参数的 IP 类规则,含义是不为匹配该 IP 规则主动解析域名,并不是关闭整个 Clash 或 mihomo 的 DNS。看到配置中出现 IP-CIDR,...,no-resolve 时,不应把所有域名解析故障归因于这个参数。
规则日志比模式切换更有信息
Rule 模式下,内核会按配置中的规则顺序匹配请求。日志通常能显示目标域名或 IP、命中的规则、使用的策略组和最终出口。若浏览器与终端访问同一域名却命中不同规则,常见原因包括终端先把域名解析为 IP、应用连接了不同子域名、IPv4 与 IPv6 结果不同,或者两者并未进入同一个内核端口。
将模式临时切换为 Global 只能用于缩小范围:如果 Global 成功、Rule 失败,应检查规则和策略组;如果两种模式都失败,则继续检查端口、DNS、节点与网络路径。完成测试后应恢复原模式,不要把长期使用 Global 当作规则问题的修复方案。
TUN 能解决什么,不能替代什么
系统代理主要服务于愿意读取操作系统代理设置的应用。TUN 模式则通过虚拟网络接口接管更广泛的 IP 流量,因此一些不支持 HTTP 或 SOCKS5 代理的终端程序,在 TUN 开启后可能恢复连接。这个现象说明原来的应用层代理入口没有覆盖该程序,不代表系统代理按钮本身损坏。
开启 TUN 前,应在客户端的「设置」→「TUN 模式」或对应网络设置中确认权限要求。Windows 可能需要服务模式或管理员权限,macOS 可能要求批准 VPN 配置或网络扩展,Linux 通常涉及网络管理权限、路由与 DNS 配置。不同客户端基于 mihomo 的实现入口不同,应以客户端当前界面和日志为准。
按结果收敛问题,而不是重复切换开关
完成上述检查后,可以用下面的顺序形成可复现结论。每一步都记录命令、端口与日志时间点,后续更换客户端或配置时也能快速复测。
- 在客户端确认当前内核处于运行状态,并记下 HTTP、SOCKS5 或 Mixed Port 的实际端口。
- 用
lsof、ss或Get-NetTCPConnection确认该端口确实由预期进程监听。 - 使用
curl -x或curl --proxy socks5h://显式连接代理,观察 Clash 日志是否出现对应请求。 - 显式代理成功后,再检查
HTTP_PROXY、HTTPS_PROXY、ALL_PROXY与大小写变量。 - 仅某个工具失败时,读取 Git、npm、pip、编辑器或构建工具自己的配置来源。
- 域名失败而代理侧解析成功时,转向系统 DNS、fake-ip 回流和应用自定义 DNS。
- 只有 TUN 成功时,确认目标程序原本是否支持系统代理,并检查终端、容器或远程环境的网络边界。
一个典型结果是:Clash 的 Mixed Port 实际为 7897,浏览器读取了客户端刚写入的系统代理,所以访问正常;终端中的 HTTPS_PROXY 仍指向 127.0.0.1:7890,Git 又在用户配置中保存了同一个旧端口。修复顺序应是先把终端变量改为 7897,再删除或更新 Git 的覆盖项,最后用显式代理和普通 Git 请求分别复测。这个过程不需要反复切换 Rule、Global 或 TUN。
另一个常见结果是:HTTP 代理测试失败,但 socks5h://127.0.0.1:7891 成功。此时先确认 HTTP 端口是否存在,不要把 SOCKS5 端口当作 HTTP 端口;若普通 SOCKS5 失败而 socks5h 成功,则继续处理本机 DNS。通过这种分层对照,可以把“Clash 不生效”缩小为明确的端口、应用配置、解析或规则问题。