Clash 系统代理已开启,为什么浏览器仍然直连?
显式指定 7897 端口可以访问,系统代理却始终不生效。沿着浏览器、Mihomo 与活动拨号连接逐层验证,最终定位 WinINET 的连接级代理配置。
FIELD NOTES / 排障速览
- 故障现象
- 显式代理正常,浏览器默认直连
- 定位关键
- 核对活动 PPPoE 连接的准确名称
- 修复闭环
- 连接级配置 → 真实请求 → 开关验证
问题摘要:电脑通过光猫有线连接,并由 Windows PPPoE 拨号上网。Clash Verge Rev 的 System Proxy 显示已开启,Windows 代理页面也显示
127.0.0.1:7897,但 Chrome / Edge 正常启动时未进入 Mihomo;显式指定代理端口却完全正常。最终发现活动 PPPoE 连接的 WinINET 连接级代理配置没有启用手动代理。使用 Windows WinINet API 按准确连接名修复后,浏览器无需指定参数即可访问 Google,Mihomo Connections 出现相应流量,且 Clash 的系统代理开关能够在代理与直连间正常切换。
0. 环境与故障表现
| 项目 | 实测环境 |
|---|---|
| 系统 | Windows,光猫有线接入,Windows PPPoE 拨号 |
| 客户端 | Clash Verge Rev v2.4.7 |
| 内核 | Mihomo v1.19.21 |
| 本地 HTTP 代理 | 127.0.0.1:7897 |
| Clash 模式 | System Proxy 开、TUN 关、DNS Override 关 |
| 活动拨号连接名 | Boradband(原中文连接重命名后的拼写;不是 Broadband) |
| 浏览器 | Chrome / Edge |
初始症状:
- Clash Verge 的系统代理开关处于开启状态,Windows 设置中也能看到
ProxyEnable=1和ProxyServer=127.0.0.1:7897。 - Chrome 和 Edge 直接打开 Google 失败,Mihomo 的 Connections 没有 Google 记录。
curl显式指定代理、或者给 Chrome 添加--proxy-server参数后,Google 可以正常访问。- Wi-Fi 环境表现与光猫有线、PPPoE 拨号环境不同,提示可能存在按连接分别存储的代理配置。
排障目标:不依赖 Chrome 固定启动参数,也不启用 TUN;开启 Clash 时浏览器进入 Mihomo,关闭 Clash 系统代理时恢复直连。
1. 先建立正确的网络模型
Mihomo Rule 决策
DIRECT → PPPoE Boradband
PROXY → 代理节点
绕过本地 HTTP 代理
经 PPPoE Boradband 出站
↓ INTERNET / 目标网站
查看原始 Mermaid 流程图代码
graph TD
A[Chrome / Edge] --> B{Windows 对当前 URL 的代理选择}
B -->|System Proxy ON 且配置有效| C[127.0.0.1:7897]
B -->|System Proxy OFF / 选择 DIRECT| D[Windows 原始网络]
C --> E[Mihomo Rule]
E -->|DIRECT| F[PPPoE Boradband]
E -->|PROXY| G[代理节点]
D --> F
G --> H[Internet]
F --> H
核心区分:Clash 的 System Proxy 是应用层的代理配置,并不等同于网卡级拦截;ping 使用 ICMP,通常不会经过 HTTP 代理。PPPoE 是逻辑拨号接口,虽然底层借助以太网,但可以拥有与默认 LAN 不同的代理配置。
2. 确认代理服务器与节点本身无故障
先确认本地端口是否监听:
Get-NetTCPConnection -LocalPort 7897 -State Listen
再明确要求 curl.exe 使用代理,而不是依赖 Windows 系统代理:
curl.exe -4 -I -x http://127.0.0.1:7897 https://www.google.com
curl.exe -4 -I -x http://127.0.0.1:7897 https://www.youtube.com
本次实测:返回 HTTP/1.1 200 Connection established,随后网站返回成功状态。说明 7897 → Mihomo → 代理节点 → 目标网站 这条链路能够工作。
注意:
curl.exe不一定会自动采用 Windows 的 WinINET 系统代理,所以“不带-x的 curl 失败”不能直接证明 System Proxy 的功能异常。
随后给 Chrome 临时增加启动参数进行对照:
--proxy-server=http://127.0.0.1:7897
结果:Google 可以打开,Mihomo Connections 能看到连接;百度匹配 DIRECT。这同时验证了浏览器访问本地代理能力、Mihomo 规则和出站链路。后续诊断应聚焦“系统代理配置如何被应用读取”,不再反复排查节点。
3. 默认系统代理“看起来正常”,不等于拨号连接代理生效
检查通常可见的 WinINET 注册表值:
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' |
Format-List ProxyEnable,ProxyServer,AutoConfigURL,ProxyOverride
当时输出类似:
ProxyEnable : 1
ProxyServer : 127.0.0.1:7897
AutoConfigURL :
ProxyOverride : localhost;127.*;192.168.*;10.*;172.16.*;...;<local>
ProxyOverride 没有包含 Google,也没有全局通配符 *。然而,Chrome / Edge 不带参数时仍未进入 Mihomo。
使用 .NET 系统代理解析器做只读交叉验证:
[System.Net.WebRequest]::GetSystemWebProxy().GetProxy(
[Uri]'https://www.google.com'
).AbsoluteUri
修复前返回:
https://www.google.com/
这里的原始 URL 表示该解析器对这个 URL 选择 DIRECT。它不是真正的 HTTP 请求,不代表 Google 已经连通;也不能单独代表所有浏览器的行为,但与 Chrome / Edge 未进入代理的现象一致。
netsh winhttp show proxy显示“直接访问”也不足以否定浏览器系统代理:WinHTTP 代理与用户的 WinINET/Internet Settings 配置不是同一个设置来源。
4. 查出真正的 PPPoE 连接名称
先列出当前活动的 Windows 拨号连接:
rasdial
本次最终确认:
已连接
Boradband
注意拼写差异:
Boradband:真正活动的旧 PPPoE 连接,名字是重命名后留下的拼写。Broadband:此前尝试创建另一连接时留下的独立名称/相关代理记录。- 不能仅凭注册表中存在某个名称,就认定对应拨号连接仍在使用。
也可以查看用户电话簿中的拨号条目(只输出名称,不涉及密码):
$pbk = "$env:APPDATA\Microsoft\Network\Connections\Pbk\rasphone.pbk"
Select-String -Path $pbk -Pattern '^\[.+\]$' |
ForEach-Object { $_.Line }
本次 rasphone.pbk 中存在 [Boradband];但最终确定活动连接仍以 rasdial 为准。
5. 读取 WinINET 的“连接级”代理标志
Windows 允许 LAN、拨号和 VPN 连接使用各自的代理设置。相关二进制配置位于:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections
可用只读 PowerShell 列出各连接代理标志:
$path = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections'
$key = Get-Item $path
foreach ($name in $key.GetValueNames()) {
$data = $key.GetValue($name)
if ($data -is [byte[]] -and $data.Length -ge 12) {
$flags = [BitConverter]::ToUInt32($data, 8)
[PSCustomObject]@{
Name = $name
Flags = ('0x{0:X2}' -f $flags)
ProxyEnabled = [bool]($flags -band 0x02)
AutoConfig = [bool]($flags -band 0x04)
AutoDetect = [bool]($flags -band 0x08)
}
}
}
关键实测结果:
| 名称 | Flags | 解释 |
|---|---|---|
Boradband(活动连接) |
0x08 |
自动检测开启,手动代理未开启 |
Broadband(非活动的测试名称) |
0x03 |
直连能力 + 手动代理开启 |
DefaultConnectionSettings |
0x03 |
默认连接已启用手动代理 |
常用标志:0x01 = PROXY_TYPE_DIRECT,0x02 = PROXY_TYPE_PROXY,0x04 = 自动脚本,0x08 = 自动检测;0x03 通常表示 0x01 | 0x02。上面的偏移 8 是针对本次 WinINET Connections 二进制格式的诊断读取,不应推广为所有 Windows 网络配置的通用格式。
到这里,故障已经有了非常明确的解释:默认代理配置和错误名称 Broadband 都已开启手动代理,但真正活动的 Boradband 没有。这比“Windows 代理设置显示开启”更有诊断价值。
6. 修复:通过 WinINet API 精确设置活动拨号连接
6.1 先备份
reg.exe export "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Connections" "$env:USERPROFILE\Connections-before-fix.reg" /y
备份文件保存在当前用户主目录。不要直接删除 Boradband、DefaultConnectionSettings 或 SavedLegacySettings;也不要在不了解二进制结构的情况下手改字节。
6.2 用 InternetSetOption 写入指定连接
微软提供 INTERNET_OPTION_PER_CONNECTION_OPTION(值为 75),用于针对特定拨号连接更新代理配置。本次通过 PowerShell 调用 WinINet API,写入三个选项:手动代理标志、代理服务器、绕过列表,并通知设置变更。
仅适用于连接名称与代理端口已确认的环境;执行前把 Boradband 改成你实际的 rasdial 连接名。
发布校订:以下示例依据微软结构定义补齐
dwOptionError与完整的选项值联合体,避免将指针大小误当成原生联合体大小。文章保留原案例实测结论;校订后的代码未在本次发布中执行或进行网络配置写入验证。
$source = @'
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
public static class DialProxyFix {
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct ConnList {
public int dwSize;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszConnection;
public int dwOptionCount;
public int dwOptionError;
public IntPtr pOptions;
}
[StructLayout(LayoutKind.Explicit)]
public struct OptionValue {
[FieldOffset(0)] public uint dwValue;
[FieldOffset(0)] public IntPtr pszValue;
[FieldOffset(0)] public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
}
[StructLayout(LayoutKind.Sequential)]
public struct ConnOption {
public int dwOption;
public OptionValue value;
}
[DllImport("wininet.dll", EntryPoint="InternetSetOptionW", SetLastError=true)]
static extern bool SetConnection(
IntPtr handle, int option, ref ConnList list, int length);
[DllImport("wininet.dll", EntryPoint="InternetSetOptionW", SetLastError=true)]
static extern bool Notify(
IntPtr handle, int option, IntPtr buffer, int length);
public static void Enable(string connection, string proxy, string bypass) {
int itemSize = Marshal.SizeOf(typeof(ConnOption));
IntPtr items = Marshal.AllocHGlobal(itemSize * 3);
IntPtr server = Marshal.StringToHGlobalUni(proxy);
IntPtr exclusions = Marshal.StringToHGlobalUni(bypass);
try {
ConnOption[] options = {
new ConnOption { dwOption=1, value=new OptionValue { dwValue=3 } },
new ConnOption { dwOption=2, value=new OptionValue { pszValue=server } },
new ConnOption { dwOption=3, value=new OptionValue { pszValue=exclusions } }
};
for (int i = 0; i < options.Length; i++) {
Marshal.StructureToPtr(
options[i], IntPtr.Add(items, i * itemSize), false);
}
ConnList list = new ConnList {
pszConnection=connection,
dwOptionCount=3,
pOptions=items
};
list.dwSize = Marshal.SizeOf(typeof(ConnList));
if (!SetConnection(IntPtr.Zero, 75, ref list, list.dwSize))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Notify(IntPtr.Zero, 39, IntPtr.Zero, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
if (!Notify(IntPtr.Zero, 37, IntPtr.Zero, 0))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally {
Marshal.FreeHGlobal(items);
Marshal.FreeHGlobal(server);
Marshal.FreeHGlobal(exclusions);
}
}
}
'@
Add-Type -TypeDefinition $source
$bypass = (Get-ItemProperty `
'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings').ProxyOverride
[DialProxyFix]::Enable('Boradband', '127.0.0.1:7897', $bypass)
代码里的 3 是 PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY,不是“强制所有流量走节点”;站点进入 Mihomo 后仍由 Rule 决定 DIRECT 还是 PROXY。这段代码调整的是 WinINET 代理选项,不是 PPPoE 拨号账号、DNS 或路由。若同一 PowerShell 会话重复执行 Add-Type,可能出现类型已存在的错误,应使用新会话或避免重复定义。
本次曾尝试在
inetcpl.cpl → 连接 → 拨号连接设置中勾选代理,但设置无法稳定保留。删除部分乱码名称注册表记录也没有立即解决问题。最终起作用的是针对准确的活动连接名写入连接级代理选项。不要把“删除乱码项”当作必经步骤。
7. 修复验证:三层闭环
第一层:系统代理解析
[System.Net.WebRequest]::GetSystemWebProxy().GetProxy(
[Uri]'https://www.google.com'
).AbsoluteUri
修复后、System Proxy 开启时返回:
http://127.0.0.1:7897/
第二层:浏览器真实请求
Chrome 不添加 --proxy-server 参数,正常打开 Google:网页可以访问;Mihomo Connections 出现 Google 请求。这比单独看注册表或 .NET 代理解析器更有说服力。
第三层:Clash 开关同步
| 操作 | .NET 代理解析器返回 | 实测判断 |
|---|---|---|
关闭 Clash System Proxy |
https://www.google.com/ |
选择直连 |
重新开启 Clash System Proxy |
http://127.0.0.1:7897/ |
选择代理 |
上述开关行为已实测成功。由此确认:本次的浏览器系统代理接入问题已修复。不过,完整退出并重新启动 Clash/Windows 后的持久性属于另一个可选回归测试,不能凭开关成功自动推断已通过重启测试。
8. 重要误区与经验
ProxyEnable=1≠ 活动拨号连接已经启用代理。 先核实rasdial的实际连接名称,再查询对应连接的 WinINET 代理选项。Broadband≠Boradband。 字符串哪怕只差一个字母,都会对应不同的连接级配置。旧名称、中文乱码或测试残留项不能仅凭外观直接删除。ping google.com丢包 ≠ HTTP 代理不可用。 Ping 走 ICMP,不走本地 HTTP 代理。实测 Ping 超时,但 Chrome 代理已恢复正常。- 显式
curl.exe -x是重要的隔离实验。 能迅速证明本地端口、Mihomo 和代理节点是否可用。 - System Proxy 与 TUN 不是一回事。 本问题最后不需要启用 TUN;TUN、Fake-IP 和 DNS 复写不是本次修复的必要条件。
- DNS 问题独立处理。 排查期间发现明文 DNS 查询和经代理 DoH 的结果不同,但它不能直接解释“请求完全不进入 Mihomo”。不要把两个故障混成一个。
- 浏览器访问成功还不够。 应结合 Mihomo Connections 确认确实进入代理服务,再测试开关关闭后能否恢复 DIRECT。
9. 可复用的故障决策表
| 步骤 | 检查 | 不同结果对应方向 |
|---|---|---|
| ① | Get-NetTCPConnection -LocalPort 7897 -State Listen |
未监听:查 Mihomo 启动、端口;已监听:继续 |
| ② | curl.exe -x http://127.0.0.1:7897 ... |
失败:查节点/出站;成功:排除代理服务本身 |
| ③ | Chrome 临时 --proxy-server |
成功:重点查系统代理接入;失败:查浏览器到本地端口 |
| ④ | rasdial |
确认真实 PPPoE 连接名,防止修错条目 |
| ⑤ | Connections 下每连接 flags |
活动连接无 0x02:修复连接级代理;有:进一步查配置解析 |
| ⑥ | GetSystemWebProxy().GetProxy(...) |
返回原 URL:该解析器选择直连;返回 7897:代理选择正确 |
| ⑦ | Chrome + Mihomo Connections | 有连接才证明实际进入 Mihomo |
| ⑧ | 关闭/开启 System Proxy 两轮验证 | 确认能自动从 DIRECT 切换为代理并恢复 |
10. 回滚与风险控制
- 修改前导出
Connections注册表键;不要随意删除活动连接的注册表值或 PPPoE 拨号配置。 - 如需撤销手工修改,可在了解覆盖范围后使用备份
.reg恢复;注册表导入会覆盖该键内的现有值,可能撤销此后产生的有效代理更改,操作前应确保备份可靠。 - 当前配置中已验证的自动切换功能,不需要再手动清理
Boradband。 - 公开博客截图请打码:PPPoE 拨号账号、密码、代理订阅地址、节点凭据等。
11. 最终结论
本次故障不是 Mihomo 代理节点不可用,也不是浏览器无法连接 7897,而是 Windows 活动 PPPoE 连接 Boradband 的连接级 WinINET 代理选项未启用手动代理。默认系统代理值和另一条错误名称 Broadband 的记录虽然正常,却不能代表当前拨号连接。
通过官方 WinINet API 对真实活动连接名写入代理设置并刷新配置后,Windows 系统代理解析器返回 127.0.0.1:7897,Chrome 无启动参数可以打开 Google,Mihomo Connections 出现请求,Clash System Proxy 开/关分别对应代理/直连。这是本次已验证的修复闭环。
尚未证明的部分:Clash Verge 最初为何没有同步写好该连接的配置(连接名称编码、历史残留、版本行为等均可能参与);不能仅凭本次修复断言某个程序版本存在确定缺陷。