WSL 代理自由切换记
从被动继承,到把代理、直连、apt 和多套本地端口真正握回自己手里

真正让我决定把这件事写下来,不是因为 WSL 代理有多复杂,而是因为它表面上看起来“能用”,实际上一直处在一种很脆弱的偶然状态里。外网能不能连,apt 会不会失败,本地的 LM Studio 能不能被 OpenClaw 调到,似乎都取决于 Windows 那边某个代理软件今天是不是刚好开着。
这种状态最浪费时间。它不会让我们立刻报废,但会不停吞掉注意力:一次 curl 失败,一次 apt update 卡住,一次以为是模型服务没起,后来才发现其实是网络路径又变了。问题不在某一条命令上,而在于整个环境缺少“我自己知道现在到底走的是哪条路”这件事。
现象
最早暴露问题的,是 WSL 里一些行为看上去互相矛盾。
一方面,终端里明明没有在 ~/.bashrc 里写代理,curl -I https://bun.sh 却能返回 HTTP/1.1 200 Connection established 以及后续的 200 响应;另一方面,一旦 Windows 上那套指定的代理软件关掉,WSL 里的网络请求又会整体失效。本地环境里还叠加了另一个条件:装在 WSL 里的 OpenClaw,要调用 Windows 侧 LM Studio 暴露的 http://127.0.0.1:5868/,而这条链路在某些时候居然也依赖那套代理是否打开。
这说明问题已经不是单纯的“有没有代理”,而是 WSL 里的网络行为并不完全由我显式配置,而是混入了外部继承、程序自身读取环境变量、apt 独立配置,以及本地 localhost 访问路径这几件事。
第一轮误判
最开始很自然地会怀疑:是不是我以前在 ~/.bashrc、/etc/environment、/etc/profile 之类的位置写过代理,只是自己忘了。
于是先查了最直观的地方。echo $http_proxy 能看到 http://127.0.0.1:10808,但 grep -i proxy ~/.bashrc 却是空的,再往 /etc/environment、/etc/profile、/etc/profile.d/、~/.profile 里搜,也没有明确的代理设置。到这里,线索开始变得清楚:变量存在,但不是从当前 Linux 用户手工写进去的。
证据
真正有价值的证据不是某一条花哨命令,而是几组朴素的对照。
第一组对照,是 proxy_on 和 proxy_off 之后直接用 curl 测站点。开代理时访问 https://bun.sh,能够稳定返回 200;切成直连后访问清华镜像,也能够直接返回 200。这说明 WSL 内部通过环境变量切换网络出口这件事,本身是成立的。
第二组对照,是 apt 的行为。把 apt 单独设成直连时,sudo apt update 可以顺利命中 mirrors.tuna.tsinghua.edu.cn;切到 Hiddify 之后,再看 apt_proxy_status,能明确看到:
Acquire::http::Proxy "http://127.0.0.1:12334/";
Acquire::https::Proxy "http://127.0.0.1:12334/";这说明 apt 不是“神秘地自动跟着 shell 走”,而是被明确写到了自己的配置文件里。它和普通命令行程序的行为层级,本来就不是一回事。
第三组对照,是新开终端和旧终端的差异。旧终端里执行 use_direct 之后,当前会话会进入直连模式;但新开一个 WSL 终端,又会因为 .bashrc 末尾默认执行了 proxy_v2rayn_on,回到默认代理状态。到这里,范围也清楚了:代理命令会影响当前 shell 会话,而 .bashrc 决定每个新会话的默认值。
根因
根因其实分成两层。
第一层,是 WSL 环境里原本存在从 Windows 侧继承下来的代理变量。这类变量不一定能在 ~/.bashrc 里找到,却会在 shell 启动时已经出现在环境里。于是表面上看,像是“WSL 自己天生就能上网”,实际上只是 Windows 那边某个本地代理端口刚好被继承进来了。
第二层,是 不同程序读取代理配置的方式根本不同。像 curl、很多 Python/Node 工具、OpenClaw 这类程序,通常会读取 http_proxy、https_proxy、all_proxy;而 apt 更偏向自己的配置文件,需要单独控制。再加上本地服务 127.0.0.1 这类地址本该永远直连,如果不靠 NO_PROXY 把它排出去,本地 API 也可能被误送进代理。
换句话说,之前最混乱的地方,不是“代理坏了”,而是 控制权不在自己手里。网络偶尔可用,只是因为几层配置碰巧叠在一起,而不是因为环境被真正整理过。
修复思路
这次修复没有追求某种“最炫的自动化”,而是追求三件事:可见、可切、可验证。
第一步,是在 ~/.bashrc 里把 Windows 可能继承下来的代理变量先清掉,再定义一组明确的函数:proxy_v2rayn_on、proxy_clashparty_on、proxy_clashverge_on、proxy_hiddify_on、proxy_alt_on、proxy_off、proxy_status。这样,WSL 里普通程序到底走哪条代理,不再由宿主机偷偷决定,而是由当前 shell 会话里的明确命令决定。
第二步,是把 apt 从“含混地跟随 shell”里剥离出来。新增 apt_proxy_on、apt_proxy_off、apt_proxy_status,让 apt 始终通过 /etc/apt/apt.conf.d/01proxy 这一个出口被管理。这样一来,中国镜像源就能长期直连,而国外源或偶发下载问题又可以随时切回代理。
第三步,是把常用组合封装成 use_v2rayn、use_clashparty、use_clashverge、use_hiddify、use_alt、use_direct。这样每天真正需要记住的,不再是很多底层变量,而只是几个工作流级别的动作:今天我是在“国内直连模式”下干活,还是在“某一套代理模式”下拉外网资源。
现在这套的详细使用说明
现在这套配置已经跑起来了,这份 面向“日常使用”的说明书,可以当小手册用。
一、我们现在拥有什么
在 WSL 里,我们有三类“开关”:
- 通用代理(影响 curl/pip/git/OpenClaw 等程序)
proxy_v2rayn_onproxy_clashparty_onproxy_clashverge_onproxy_hiddify_onproxy_alt_onproxy_offproxy_status
这些控制的是环境变量 http_proxy / https_proxy / all_proxy,大多数命令行程序都会跟着走。
- apt 专用
apt_proxy_onapt_proxy_offapt_proxy_status
控制 /etc/apt/apt.conf.d/01proxy,只影响 apt update / apt install。
- 组合快捷键(通用代理 + apt 一起切)
use_v2raynuse_clashpartyuse_clashvergeuse_hiddifyuse_altuse_direct
一条命令同时切换“WSL 里的所有程序”和 apt,最省心。
二、最常用的 3 个场景
场景 1:只用中国镜像 / 本地服务(推荐日常默认)
目标:
- WSL 直连国内源和 LM Studio、各种本地端口
- apt 只走中国镜像,不走梯子
做法:
use_direct这会自动做两件事:
proxy_off→ 所有程序直连(但localhost/127.0.0.1永远直连,本来就不走代理)。apt_proxy_off→ apt 配置改成“完全直连”。[^27_5][^27_6]
可选检查:
proxy_status # 显示 当前:直连模式
apt_proxy_status # 显示 当前:直连模式适合做什么:
- 用清华/中科大镜像
sudo apt update && sudo apt upgrade - 访问
http://127.0.0.1:5868(LM Studio) - 拉国内源、国内 Git 仓库
场景 2:所有东西都走 v2rayN(需要外网)
目标:
curl / pip / git / OpenClaw等全部走 v2rayN 的 10808- apt 拉包也走 v2rayN
做法:
use_v2rayn相当于:
proxy_v2rayn_on→ 设置http_proxy=http://127.0.0.1:10808等。apt_proxy_on→ apt 也用当前代理(这时就是 10808)。[^27_3][^27_1]
检查:
proxy_status # [WSL] 当前代理: v2rayN -> http://127.0.0.1:10808
apt_proxy_status # Acquire::http::Proxy "http://127.0.0.1:10808/";适合:
- 拉 GitHub / PyPI / npm / 外网 deb 仓库
- OpenClaw 调用外网 API(前提是它读环境变量)
场景 3:v2rayN 不稳,切到 Hiddify/Clash 之类的备线
假设我们发现 10808 很容易重置,想换 Hiddify 的 12334:
use_hiddify相当于:
proxy_hiddify_on→ 通用代理改为127.0.0.1:12334apt_proxy_on→ apt 也用这个端口。[^27_7][^27_3]
检查:
proxy_status
# [WSL] 当前代理: Hiddify -> http://127.0.0.1:12334
apt_proxy_status
# Acquire::http::Proxy "http://127.0.0.1:12334/";
# Acquire::https::Proxy "http://127.0.0.1:12334/";同理,如果我们想让一切都走 Clash Party 或 Clash Verge:
use_clashparty # 走 127.0.0.1:7890
use_clashverge # 走 127.0.0.1:7897
use_alt # 走 127.0.0.1:10809(我们备用端口)(前提是 Windows 里对应软件/端口已经启动并监听。)
三、细一点的使用说明
1)通用代理函数(不动 apt)
如果我们只想让某些程序走代理、apt 保持原样,可以单独用这些:
proxy_v2rayn_on # 改成 v2rayN
proxy_clashparty_on # 改成 Clash Party
proxy_clashverge_on # 改成 Clash Verge
proxy_hiddify_on # 改成 Hiddify
proxy_alt_on # 改成备用端口
proxy_off # 完全直连
proxy_status # 当前代理情况- 改完后,在 这个终端里新启动的程序 都会用新的代理设置。
- 已经在跑的进程一般不会“自动跟着变”,除非它重新读取环境变量。
2)apt 专用函数
如果我们只想改变 apt 的行为,不动其他程序:
apt_proxy_on # 让 apt 用“当前通用代理”的 http 地址
apt_proxy_off # 让 apt 完全直连
apt_proxy_status # 看 apt 现在用不用代理注意:apt_proxy_on 会读 CURRENT_PROXY_HTTP,也就是我们之前用 proxy_xxx_on 或 use_xxx 选的那个端口;如果当前没选就默认 10808。[^27_4][^27_3]
四、推荐“心智模型”
我们可以这样在脑子里简化成两层开关:
- 上层:我现在的终端要走哪条线?
- 直连:
proxy_off或use_direct - v2rayN:
proxy_v2rayn_on或use_v2rayn - Clash Party:
proxy_clashparty_on或use_clashparty - Clash Verge:
proxy_clashverge_on或use_clashverge - Hiddify:
proxy_hiddify_on或use_hiddify
- 直连:
- 下层:apt 要不要跟着走?
- 跟着当前代理:
apt_proxy_on或直接用use_xxx - 只用中国镜像:
apt_proxy_off或use_direct
- 跟着当前代理:
只要我们记住这两层,基本不会再被“到底走没走代理”“为什么 apt 又拉不动包”这类问题绕住。
五、出问题时怎么看
如果我们拉东西失败,排查顺序可以用这几条:
- 看 WSL 代理状态:
proxy_status- 看 apt 状态:
apt_proxy_status- 用 curl 测一下:
curl -I https://bun.sh # 测外网
curl -I https://mirrors.tuna.tsinghua.edu.cn/ubuntu # 测国内镜像- 检查我们要用的代理软件在 Windows 里有没有真的开着、端口对不对。
最终落地
最终保留下来的配置,并不复杂,但很够用。
普通程序层面,用的是环境变量切换:
proxy_v2rayn_on
proxy_hiddify_on
proxy_off
proxy_statusapt 层面,用的是独立状态切换:
apt_proxy_on
apt_proxy_off
apt_proxy_status日常真正高频使用的,是组合命令:
use_direct
use_v2rayn
use_clashparty
use_clashverge
use_hiddify这里面,use_direct 会同时让 shell 和 apt 进入直连,适合中国镜像、本地 LM Studio 以及各种不需要外网代理的场景;而 use_hiddify、use_v2rayn 这些命令,则同时切换普通程序和 apt 的出口,适合拉外网资源、安装海外仓库、或者在某条线路不稳时迅速换路。
经验
这次最大的经验,不是某个命令怎么写,而是:不要把“碰巧能用”误当成“已经配置清楚”。
WSL 这种环境特别容易出现一种错觉:今天能上网、今天能拉包、今天本地 API 也能通,于是就先用着。但只要控制权不在自己手里,后面一定会用更高的时间成本把这笔账补回来。
真正稳妥的做法,是给每一层都留出清楚的开关和状态检查。能直连的,明确直连;必须代理的,明确代理;apt 单独管;本地 localhost 永远排除;新终端默认值写清楚。这样以后再遇到问题,不需要猜,只需要看状态。
梦行志

