为什么 WSL2 里「代理开了」仍连不上

很多同学习惯在 Windows 桌面运行 Clash(或基于 mihomo 的衍生客户端),浏览器里一切正常;一打开 WSL2 里的 Ubuntu 子系统,apt updategit clonecurl 却照样超时。根因通常不是「Clash 坏了」,而是三类事实被混在一起:网络命名空间不同监听地址写错、以及DNS 仍在子系统里走直连解析

WSL2 本质是轻量虚拟机,子系统里的 127.0.0.1 指向的是子系统自己,而不是你 Windows 宿主机上 Clash 监听的 loopback。把代理写成 http://127.0.0.1:7890,等于让流量去敲一扇「子系统本地的空门」。另一方面,即便你把代理指到了正确的宿主机 IP,若域名解析在 WSL2 里仍落到不可达的 DNS,或解析结果被策略污染,也会出现「HTTP 隧道能建、握手却怪异地失败」的现象。

下文按排障顺序写:先 Windows 侧端口与防火墙,再取宿主机在 WSL2 视角下的可达地址,然后配置通用代理环境变量与可选 no_proxy,最后用 curl 做分层自检,并把 DNS 从症状里拆出来。若你计划在 Linux 里直接跑内核而不是借用 Windows,可对照站内 Ubuntu systemd 安装教程;Windows 本机首次安装与订阅导入见 Windows 安装指南

Windows 侧:mixed-port、监听地址与防火墙

Clash 常见会把 HTTP 与 SOCKS 合并到 mixed-port(例如配置文件里的 mixed-port: 7890)。在 Windows 上,你需要确认两件事:第一,端口确实是 Clash 在占用,而不是别的程序偶然抢占了同名端口;第二,监听是否绑定在 仅 127.0.0.1 还是 0.0.0.0(或等价地,允许来自虚拟网卡的入站)。

若监听只绑在 127.0.0.1,从 WSL2 过来的连接会被操作系统拒绝——因为来源 IP 属于虚拟网卡网段,而不是本机环回。此时要么在客户端或配置里开启「允许局域网连接」一类选项(底层往往对应 allow-lan 与合适的 bind-address),要么显式让代理监听 0.0.0.0 的指定端口。更细的字段与防火墙入站规则,可与 局域网共享与防火墙教程对照;那里用「手机连电脑」的场景讲清了 netstat 与 Windows Defender 入站放行思路,WSL2 访问宿主机是同一类「跨接口入站」问题。

改完监听地址后,在 Windows 上用 netstat -ano | findstr 7890(端口号换成你的 mixed-port)核对 LISTEN 行是否落在 0.0.0.0 或包含虚拟网卡可路由的地址,再回 WSL2 复测。

在 WSL2 里拿到「正确的」Windows 宿主机地址

社区里最稳的做法之一,是读 WSL2 自动写入的解析器地址:cat /etc/resolv.conf 里的 nameserver 行,往往就是从子系统视角看到的 Windows 宿主机在虚拟交换机上的 IP。用它作为 HTTP_PROXY 里的主机段,通常比猜固定网段更可靠。

也有人尝试 $(hostname).local 一类 mDNS 名称。它在部分网络与版本组合下可用,但并非总是解析到宿主机;排障时建议把它当作「锦上添花」,而不是唯一依赖。另一个常见误区是照搬「默认网关」思路:WSL2 的默认路由指向的是虚拟网关,不一定等于你要连的 Clash 监听口所在接口,直接当代理主机用容易踩坑。

实务上可以「双通道验证」:先用 pingnc -vz <host> <port> 测宿主机 IP 与 mixed-port 是否 TCP 可达;再在同一终端里导出代理环境变量跑 curl。若前者不通而后者的应用层当然也不会 magically 变好。

代理环境变量:HTTP_PROXY、HTTPS_PROXY、ALL_PROXY 与 no_proxy

大多数 CLI 工具会认大写的 HTTP_PROXY / HTTPS_PROXY,不少实现也会读小写同名变量。为减少「某个工具只认小写」的玄学问题,可以在 shell 启动脚本里成对导出。若 Clash mixed-port 同时承载 HTTP 与 SOCKS,常见写法是把 HTTP 类流量指向 http://<windows-host>:<mixed-port>;需要 SOCKS 的工具再单独设 ALL_PROXY=socks5://...

no_proxy(以及小写 no_proxy)用于绕过内网与本地地址,避免把访问公司 Git、私有 registry、或 localhost 上的服务硬塞进代理。典型会包含 127.0.0.1,localhost,.local,.internal 以及你们内网域名后缀。注意:no_proxy 的匹配规则因工具而异,Git、curl、Python requests 各自有细节;遇到「只有某一个命令仍走代理」时,优先查该工具的专用配置而不是死磕环境变量。

将下列片段放进 ~/.bashrc~/.zshrc(把主机与端口换成你的实测值;示例端口与常见 Clash mixed-port 一致,仅为演示):

export HOST_IP=$(grep -m1 nameserver /etc/resolv.conf | awk '{print $2}')
export HTTP_PROXY="http://${HOST_IP}:7890"
export HTTPS_PROXY="http://${HOST_IP}:7890"
export http_proxy="$HTTP_PROXY"
export https_proxy="$HTTPS_PROXY"
export NO_PROXY="127.0.0.1,localhost"
export no_proxy="$NO_PROXY"

每次 Windows 重启或 WSL2 网络栈重建后,resolv.conf 里的地址可能变化;把 HOST_IP 写成「登录 shell 时动态解析」比写死 IP 更省心。若你使用 systemd 化的 WSL 发行版或自定义了 resolv.conf 生成规则,以你机器上实际文件为准。

apt 与 git:别只改环境变量就以为万事大吉

apt 有时不会完全跟随 HTTP_PROXY,尤其在某些插件或 HTTPS 源场景下。若你发现 export 后 curl 已通而 apt 仍失败,可以在 /etc/apt/apt.conf.d/ 下增加一行 Acquire::http::Proxy "http://HOST:PORT"; 与对应的 https 项,或查阅当前 Ubuntu 版本关于代理的官方说明。关键是保持「apt 看到的代理地址」与你在 WSL2 里测通的是同一个宿主机 IP。

Git 除环境变量外,还支持 git config --global http.proxy / https.proxy。当你混用 SSH 与 HTTPS 克隆时,记得 SSH 远程并不吃 HTTP_PROXY;HTTPS 克隆才走这类代理。团队内网仓库若域名落在 no_proxy 之外,可能被误送到外网节点,表现为认证反复失败或极慢——这时要回头整理 no_proxy 列表。

用 curl 做分层自检:从 TCP 到 TLS

建议按顺序跑三条命令,观察输出里「连接建立」「CONNECT tunnel」「SSL handshake」各阶段卡在哪里。

  1. 直连探测curl -v http://connectivitycheck.gstatic.com/generate_204 — 期望在没设代理时失败或符合你网络预期;用于建立基线。
  2. 显式走代理curl -v -x http://<HOST_IP>:7890 https://www.cloudflare.com/cdn-cgi/trace — 期望看到经由代理的 CONNECT,以及返回内容里出现你节点的出口特征。
  3. 仅测解析getent hosts www.example.comdig(若已安装)— 与 curl 分开观察 DNS 是否先在 WSL2 内失败。

若第二条在 CONNECT 之后卡在 TLS,多半是证书或中间人策略问题;若卡在「解析域名」,就要进入下一节的 DNS 讨论,而不是继续调端口。

DNS 陷阱:代理开了,解析仍在直连

HTTP 代理访问 HTTPS 站点时,常见路径是客户端先向代理发起 CONNECT,再由远端完成解析与出站。但在一些工具链或系统配置下,本地仍会先做 A/AAAA 查询;当企业网络或运营商 DNS 对特定域名返回「看似成功、实则不可用」的答案时,你会看到「浏览器 OK、CLI 怪」的分裂现象。

WSL2 默认从 Windows 继承 DNS 配置,有时 /etc/resolv.conf 会被自动生成或指向一个仅在内网有意义的解析器。若该解析器在子系统视角不可达,或返回被劫持的 IP,curl 会在很早阶段失败。处理思路分两层:其一是确保 resolv.conf 中的 nameserver 本身可达;其二是理解 Clash 在 Windows 侧的 DNS 处理(fake-ip、redir-host 等)与 WSL2 里命令行解析并不是同一个管道——这与站内多篇讲 fake-ip 与规则分流的技巧文是同一套逻辑,只是发生位置换到了「Windows 宿主 + 子系统」。

实务上,先把「curl 显式走代理」调通,再观察是否仍有纯解析问题;若仅在禁用系统代理的工具里失败,多半要动 resolv.conf 或考虑在受信网络环境下改用可达的公共 DNS 作为 WSL2 的上游(请遵守你所在组织的 IT 政策)。更系统的 DNS 与 TUN 总论可结合 TUN 模式指南 阅读,但记住:WSL2 里跑 TUN 与 Windows 上跑 TUN 不是一回事,本文不展开内核设备共享的细节。

常见误区清单(便于对照自查)

  • 把代理写成 127.0.0.1,却期望连到 Windows 上的 Clash。
  • Windows 侧只监听环回,未对虚拟网卡网段放行;或未开 allow-lan / bind-address 导致 WSL2 入站被拒。
  • 混用端口:把 mixed-port 与单独的 SOCKS 端口记反,或客户端 UI 显示「系统代理」但 mixed-port 实际未监听。
  • 只设置大写环境变量,而目标工具只认小写(或相反)。
  • no_proxy 过宽或过窄,导致内网流量误走外网节点,或外网流量被错误直连。
  • 忽略 DNS:代理 TCP 已通,但本地解析阶段就失败,误以为「规则没命中」。

小结

WSL2 Ubuntu 与 Windows Clash 的组合,本质是「跨虚拟网卡的客户端—服务端」问题:先把 mixed-port 在正确的接口上监听到、让子系统能用宿主机 IP 建 TCP,再用成对的环境变量把 CLI 流量送进去,最后用 curl 分层确认不是 DNS 在拖后腿。把这三步拆清楚,大多数「浏览器能上网、终端全挂」的工单都能收敛到可复现的两三条命令上。

若你更希望「一个安装包 + 图形界面」完成订阅导入、系统代理与规则可视化,而不是在两边来回拼配置,可以优先从本站分发页获取已打包的客户端;相比手工维护多份 YAML 与多个终端环境,一体化工具在状态展示与故障定位上往往更省心。

→ 立即免费下载 Clash,开启流畅上网新体验