搜索 K
Appearance
面向工程师的 Win11 代理栈拆解:不教你怎么点开关,而是告诉你开关背后改了哪个注册表项、为什么 Chrome 生效而 Steam 不生效、以及"系统代理开关失灵"到底是谁在跟你抢方向盘。
Windows 11 的"系统代理"只写 WinINet 注册表,不写 WinHTTP,也不管环境变量。 你打开设置页面里的代理开关,实际改动的是 HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings 下的 ProxyEnable / ProxyServer / AutoConfigURL 四个值。任何不走 wininet.dll 的程序(Node.js、Go 二进制、Java、部分游戏客户端)压根看不见这个开关。
"系统代理"和"TUN 模式"是两条技术路线,不是两个选项。 系统代理是应用层显式接力,只覆盖遵守 HTTP 代理约定或读取 WinINet 的进程;TUN 是网络层劫持,用 WinTUN 虚拟网卡 + 路由表覆盖全协议(含 UDP/QUIC、ICMP)。想做全局无感,只有 TUN 或透明网关。
90% 的"代理开了没网/时好时坏",根因不在机场,在本机 DNS 缓存、PAC 解析超时、UWP 环回隔离或组策略锁定。 先用 netsh winhttp show proxy 和 curl.exe -v 把链路切开,再谈节点质量。
Windows 从 NT4 时代就把网络栈拆成了两条互不相干的腿:
wininet.dll) —— 面向交互式桌面应用。IE、旧版 Edge、Office 的联网组件、大部分国产客户端、Electron 应用(Chromium 默认读系统代理)、系统设置 UI 全部走这条线。winhttp.dll) —— 面向服务与后台任务。Windows Update、BITS 后台传输、部分企业 VPN 组件、某些安装器走这条线。两者各自维护独立的代理配置。设置 UI 只改 WinINet,WinHTTP 必须用命令行改:
# 查看当前 WinHTTP 代理(跟设置 UI 完全无关)
netsh winhttp show proxy
# 设置 WinHTTP 代理(需要管理员权限)
netsh winhttp set proxy proxy-server="127.0.0.1:7890" bypass-list="localhost;127.*;10.*;172.16.*;192.168.*"
# 恢复系统默认(直连/自动发现)
netsh winhttp reset proxy这就解释了一个经典现象:浏览器能上网,但某个软件的更新检查、Steam 下载、Epic 启动器死活连不上。 它走的是 WinHTTP。注意 netsh winhttp 不允许把代理指向环回地址用于本地服务转发以外的场景,且它是机器级设置,需要管理员。
不同运行时读代理的姿势完全不一样:
| 运行时 | 代理来源 | 备注 |
|---|---|---|
| Chromium / Electron | WinINet 系统代理 + 命令行 --proxy-server | 命令行优先级更高 |
| Firefox | 自身设置(默认「使用系统代理设置」) | 可独立覆盖 |
| Node.js | HTTP_PROXY / HTTPS_PROXY 环境变量 | 完全不读系统代理 |
| Go 二进制 | 同上环境变量 | NO_PROXY 决定直连白名单 |
| Java (JVM) | -Dhttps.proxyHost 启动参数 | 或系统属性 |
| Python requests | 环境变量 / 显式传参 | urllib 部分读注册表 |
| UWP / 商店应用 | WinINet,但受 AppContainer 环回隔离限制 | 见 2.4 |
所以「Win11 网络设置」里那把开关,本质只服务了一半生态。
PAC(Proxy Auto-Config)是一个返回字符串的 JavaScript 函数 FindProxyForURL(url, host)。Win11 支持三种注入方式:
AutoConfigURL 注册表值,指向 http(s):// 或 file:/// 地址。wpad. 前缀获取。PAC 失效的三大真实原因:
text/html 而非 application/x-ns-proxy-autoconfig,WinINet 会静默拒绝执行。file:/// 路径用了反斜杠。 必须写 file:///C:/proxy.pac,写成 file://C:\proxy.pac 直接失效。explorer.exe 或直接注销。另外,PAC 的性能是有代价的:每次请求都要过一遍 JS 引擎。复杂正则 + isInNet 大量调用,在高并发(比如同时开 200 个连接)时能吃掉可观的 CPU,并引入毫秒级额外延迟。规则超过 500 条,建议直接换 TUN + 规则分流。
Windows 有安全沙箱(AppContainer)机制,默认禁止 UWP 应用访问 127.0.0.1。你的代理内核监听在本机环回地址上,商店应用自然连不上。解决办法是给它开环回豁免:
# 查看当前豁免列表
CheckNetIsolation.exe LoopbackExempt -s
# 为指定包添加豁免(示例)
CheckNetIsolation.exe LoopbackExempt -a -n="microsoft.windowscommunicationsapps_8wekyb3d8bbwe"图形化工具可以用 LoopbackExempt 的命令行包装,或者直接让代理内核监听 0.0.0.0 并在防火墙放行——但这个做法有安全代价,本机其他设备也能连你的代理端口,不建议在公共网络下使用。
系统代理只解决「流量怎么交给内核」。剩下的 90% 体验由链路决定:
mtr 全程无第三跳丢包,且路由跳数通常在 5 跳以内。结论:本机配置决定「能不能用」,链路质量决定「好不好用」。两者必须分开诊断。
| 维度 | 系统代理(手动) | PAC 脚本 | TUN 虚拟网卡 | 进程级代理(Proxifier 类) | 路由器透明代理 | 浏览器插件 |
|---|---|---|---|---|---|---|
| 接管层级 | 应用层(WinINet) | 应用层 + JS 决策 | 网络层(L3) | 进程级 socket 劫持 | 网关层 | 应用内 |
| 覆盖协议 | HTTP/HTTPS | HTTP/HTTPS | 全协议含 UDP/QUIC | TCP 为主,UDP 视内核 | 全协议 | 仅浏览器 |
| UWP/商店应用 | 受限(环回隔离) | 受限 | 完全覆盖 | 受限 | 完全覆盖 | 不覆盖 |
| DNS 处理 | 依赖应用 | 依赖应用 | 可劫持重定向 | 依赖应用 | 可劫持 | 依赖浏览器 |
| 游戏/语音延迟 | 不支持 | 不支持 | 支持 | 部分支持 | 支持 | 不支持 |
| 系统侵入性 | 极低 | 低 | 中(装驱动) | 中 | 高(需刷机) | 无 |
| 需要管理员 | 否(仅当前用户) | 否 | 是 | 视配置 | 是 | 否 |
| 典型配置耗时 | 1 分钟 | 10 分钟 | 5 分钟 | 20 分钟 | 60 分钟以上 | 1 分钟 |
| 典型额外延迟 | 接近 0 | 0–3 ms(规则复杂度相关) | 1–5 ms | 1–3 ms | 0–2 ms | 0 |
| 推荐场景 | 轻度、单浏览器 | 企业内网分流 | 全设备日用主力 | 只代理特定程序 | 全屋设备 | 临时应急 |
选型口诀:只刷浏览器用系统代理;要打游戏/看原生画质用 TUN;要管全家设备上路由器;只有一两个程序需要走代理上进程级方案。
图形路径:设置 → 网络和 Internet → 代理 → 手动设置代理 → 编辑
127.0.0.1,「端口」填你本地内核的混合端口(Clash 系常见 7890,V2Ray 常见 10809/10808)。localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;192.168.*;<local>命令行等价操作(免点鼠标、便于脚本化):
$k = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
Set-ItemProperty -Path $k -Name ProxyEnable -Value 1
Set-ItemProperty -Path $k -Name ProxyServer -Value "127.0.0.1:7890"
Set-ItemProperty -Path $k -Name ProxyOverride -Value "localhost;127.*;10.*;172.16.*;192.168.*;<local>"
# 关闭系统代理
Set-ItemProperty -Path $k -Name ProxyEnable -Value 0改完注册表后,需要通知系统刷新(否则部分程序仍读旧值):
# 触发 WinINet 刷新最可靠的方式是重启相关进程
Stop-Process -Name explorer -Force # 会重启桌面,谨慎本地 PAC 文件写法(保存为 C:\proxy.pac,注意 UTF-8 无 BOM):
function FindProxyForURL(url, host) {
// 内网直连
if (isPlainHostName(host) ||
shExpMatch(host, "*.local") ||
isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") ||
isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0")) {
return "DIRECT";
}
// 国内域名直连
if (shExpMatch(host, "*.cn") || shExpMatch(host, "*.baidu.com")) {
return "DIRECT";
}
return "PROXY 127.0.0.1:7890; DIRECT";
}填入设置 UI 的「使用脚本地址」时,本地文件必须写 file:///C:/proxy.pac。远程脚本建议自建 HTTP 服务并确保响应头为 application/x-ns-proxy-autoconfig。
PAC 的最大坑是 dnsResolve()。 它会让 WinINet 同步解析域名,在大规模并发下阻塞明显,甚至可以造成 DNS 泄漏。能不用 dnsResolve 就不用,改用 shExpMatch 和预置域名列表。
以 Clash Verge Rev / Mihomo 内核为例:
clash-verge-service,避免每次 UAC 弹窗)。gVisor(兼容性优先)或 System(性能优先),Windows 上推荐先试 gVisor。bypass 局域网:192.168.0.0/16、10.0.0.0/8、172.16.0.0/12。验证是否真的接管了:
# 查看路由表,默认路由应指向 TUN 虚拟网卡
route print 0.0.0.0
# 或
Get-NetRoute -DestinationPrefix "0.0.0.0/0" | Format-Table ifIndex, NextHop, RouteMetric如果默认路由仍指向物理网卡(NextHop 是网关 IP 而不是 0.0.0.0),说明 TUN 没接管成功,通常是内核没有以管理员/服务模式运行。
给 Node.js / Go / Python 类工具注入环境变量:
setx HTTP_PROXY "http://127.0.0.1:7890"
setx HTTPS_PROXY "http://127.0.0.1:7890"
setx NO_PROXY "localhost,127.0.0.1,::1,192.168.0.0/16"setx 只对新开的终端生效。需要立即生效就用 $env:HTTP_PROXY="http://127.0.0.1:7890"。
| 命令 | 作用 | 关键判读 |
|---|---|---|
netsh winhttp show proxy | 看 WinHTTP 层配置 | 显示 Direct access 却没网 → 服务级直连被墙 |
reg query "HKCU\...\Internet Settings" /v ProxyEnable | 看 WinINet 开关真实值 | 值为 0x0 但 UI 显示开着 → 被组策略覆盖 |
curl.exe -v -x http://127.0.0.1:7890 https://www.gstatic.com/generate_204 | 绕过一切应用层干扰,直达内核 | 返回 204 说明内核+出口正常,问题在应用侧 |
Test-NetConnection 1.1.1.1 -Port 443 | 测 TCP 可达性 | TcpTestSucceeded : False → 通道被切断 |
tracert -d -h 15 1.1.1.1 | 看路由走向 | 第 3 跳全是 * 且持续 → 上游过滤 ICMP,不代表断网 |
Resolve-DnsName google.com -Server 127.0.0.1 -DnsOnly | 验证 DNS 是否走内核 | QueryType 超时 → 内核 DNS 劫持未生效 |
netstat -ano | findstr 7890 | 看内核端口监听状态 | 无输出 → 内核根本没起 |
Get-NetAdapter | Where-Object {$_.InterfaceDescription -like "*TUN*"} | 检查 TUN 网卡 | 无结果 → TUN 未创建 |
典型现象 → 根因判定表:
| 现象 | 高概率根因 | 验证动作 | 处置 |
|---|---|---|---|
| 浏览器正常,Steam/Epic 不下载 | WinHTTP 未设代理 | netsh winhttp show proxy | 执行 netsh winhttp set proxy |
| 设置里开关自动回弹 | 组策略或安全软件锁定 Internet Settings | 查 HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings | 删除策略键或退出拦截软件 |
| 全部断网但内核日志正常 | DNS 泄漏被污染 / 内核 DNS 未劫持 | Resolve-DnsName -Server 127.0.0.1 | 打开 DNS 劫持,或用 DoH |
| 握手失败、证书报错 | 系统时间偏差超过 5 分钟 | w32tm /stripchart /computer:time.windows.com | 同步 NTP |
| 开了代理走局域网 NAS 失败 | bypass 规则未包含内网段 | route print | 补充 192.168.0.0/16 直连 |
| UWP 应用无法联网 | AppContainer 环回隔离 | CheckNetIsolation.exe LoopbackExempt -s | 添加包豁免 |
| 晚高峰丢包严重 | 公网中转线路被 QoS | 定时 mtr 对比白天/夜间 | 换 IEPL 专线类服务商 |
| 宣传话术 | 真相 | 鉴别方法 |
|---|---|---|
| 「无限流量、不限速」 | 通常配合连接数限制或晚高峰软限速 | 连续跑 30 分钟大文件下载,看速率是否 |