跳至内容

代理开着却上不了网:macOS 系统代理按网络服务生效的坑

代理软件在运行、模式是规则、节点也正常,浏览器却全部打不开——问题出在 macOS 把代理设置按网络接口分别保存,当前在用的那条是空的
2026年11月13日 由
代理开着却上不了网:macOS 系统代理按网络服务生效的坑
赵博

早上打开浏览器,境外站点一个都打不开。代理软件明明在运行,图标亮着,界面里的开关也全是打开的。直觉是去调代理模式——从规则切到全局,还是不行;换节点,也不行;重启软件,依旧不行。

最后发现问题根本不在代理软件的模式设置上:它有 105 个节点全部可用、规则分流 151 条、订阅状态都正常,但 macOS 从来没有把任何流量交给它。这篇文章把这套排查顺序和修复方法完整记下来。

先分清四层,别一上来就调模式

代理能否生效,要穿过四层。每一层都有独立的判据,混在一起看就很容易误判。

代理四层链路:客户端进程、代理内核、系统代理、浏览器

这次的情况是前两层完全正常、第三层断了。它的迷惑性在于:你在命令行里手动指定代理去访问,是通的,于是很容易得出「代理没问题」的结论。实际上命令行手动指定代理,等于绕过了第三层,验证的只是前两层。

逐层判据

← 左右滑动查看完整表格 →

层要确认什么判断命令 / 位置这次的结论
① 客户端进程软件是否真的在跑进程列表里能看到主程序正常
② 代理内核内核是否监听代理端口端口处于监听状态,且手动指定代理能出网正常
③ 系统代理系统有没有把流量转给内核系统代理快照中三个开关是否启用未启用
④ 浏览器 / 应用应用是否读到了代理设置境外站点能否打开受影响

排查顺序建议从第③层开始。因为第①②层在界面上有明确的状态显示,而第③层的真实状态界面上看不到——软件里的「系统代理」开关表达的是软件的意图,不是操作系统的实际状态,两者可以长期不一致。

第三层为什么会断:系统代理不是全局开关

这是这次问题的核心,也是最反直觉的一点。

在 macOS 上,代理设置并不存在「一个全局开关」。它是按每个网络服务(network service)分别保存的:Wi-Fi 有一份,有线网卡有一份,雷电桥有一份,iPhone USB 共享也各有一份。每一份里面再分 HTTP、HTTPS、SOCKS 三种。

系统代理按网络服务分别写入:Wi-Fi 未写入,其余接口已写入

这台机器上的实际状态是:其他三个接口都正确写入了代理地址,唯独 Wi-Fi 这一条没有。而当时正好在用 Wi-Fi(连的是手机热点),于是所有走系统的应用全部直连。

为什么会变成这样?因为代理软件写入系统代理时,写的是「当时检测到的、正在使用的接口」。插网线的时候写过,用 iPhone 共享的时候写过,Wi-Fi 这一条从来没被写过。所以会出现「昨天还好好的,今天全打不开」——不是软件坏了,是换了一条没配置过的网络服务。

确认方法

# 1. 系统代理的真实状态(三项 Enabled 全是 0 就说明没接管)
scutil --proxy

# 2. 逐个网络服务看代理配置
for s in "Wi-Fi" "USB 10/100 LAN" "iPhone USB" "Thunderbolt Bridge"; do
  echo "=== $s ==="
  networksetup -getwebproxy "$s"
done

# 3. 确认当前默认路由走的是哪个接口
route -n get default | grep interface

另外有一个判断上的坑值得单独说:不要用 lsof 去判断代理端口是否在监听。代理内核通常以 root 身份运行,普通权限的 lsof 看不到它的监听端口,输出里一片空白,很容易误判成「端口没起来」。用 netstat 查才是准的:

netstat -an -p tcp | grep LISTEN | grep -E '\.(7890|9090) '

# 也可以直接测一次出网,这一步不依赖任何界面状态
curl -s -o /dev/null -w "%{http_code}\n" --max-time 8 \
  -x http://127.0.0.1:7890 https://www.google.com/generate_204
# 返回 204 说明内核和节点都没问题,纯粹是系统没把流量转过来

修复:给当前在用的接口补上代理

把其他接口上已有的配置原样复制过来即可。这一步不需要管理员权限,因为改的是当前用户自己的网络代理偏好:

networksetup -setwebproxy        "Wi-Fi" 127.0.0.1 7890
networksetup -setsecurewebproxy  "Wi-Fi" 127.0.0.1 7890
networksetup -setsocksfirewallproxy "Wi-Fi" 127.0.0.1 7890

顺手把 bypass 列表也补上,否则本机服务、内网地址、局域网设备都会被绕进代理,出现「本地调试连不上」「内网设备访问不了」这类新问题。这个列表要和软件里已经配好的保持一致:

networksetup -setproxybypassdomains "Wi-Fi" \
  localhost "*.local" "127.*" "10.*" \
  "172.16.*" "172.17.*" "172.18.*" "172.19.*" \
  "172.20.*" "172.30.*" "172.31.*" "192.168.*"

改完立即验证,三个开关都应该是启用状态:

scutil --proxy | grep -E "HTTPEnable|HTTPSEnable|SOCKSEnable"
# 期望三项都是 1

验证:规则分流是否真的按预期工作

系统代理接上之后,不能只看「网页能打开了」就结束。还要确认流量是进了代理内核并按规则分流,而不是全部硬走代理。分开测三组:

← 左右滑动查看完整表格 →

测试目标期望结果说明了什么
国内站点直连可达,状态码 200国内流量没被绕出去
境外站点(如 Google 的 204 探针)返回 204境外流量能出网
AI 服务接口返回 401 / 403 这类业务响应链路通,只是缺凭证——这本身就是成功

其中最容易被误判成失败的是第三条。接口返回 401 说明请求已经完整到达服务器,服务器在正常回应「你没带密钥」,这恰恰证明代理链路是通的。如果真有问题,表现会是超时或连接被重置。

想看到更细的分流路径,可以查代理内核的连接列表,每一行都会标出命中的规则和实际走的节点链路:

# 例:Google 组、AI 组各自走哪个节点
# chains 字段从内到外依次是:具体节点 > 自动选择 > 手动选择 > 内容分组

顺带发现的性能问题:自动测速被钉死在单个节点

排查过程中还发现了一个独立的问题。分组类型是自动测速(url-test),但实际每次都走同一个节点。原因是这个分组被记录了一个「固定节点」,一旦固定,自动测速就不再参与挑选。

固定节点 172ms 与自动测速后 76ms 的对比

同组里 105 个节点全部可用,最快 76ms、原来固定的那个 172ms,差了一倍多。这类问题平时不会报错,只是慢,很难被注意到。

两个坑

坑一:只解除运行状态的固定,重启会回弹。固定节点的状态同时存在于两处——运行中的内核状态,和本地保存的分组选择记录。只把运行状态解除,软件下次启动或订阅自动更新时会从本地记录里读回来,又钉在旧节点上。所以必须两处都清。

坑二:解除固定之后,不会立即重新测速。状态显示已经是自动,但当前节点还是上一次的那个,要主动触发一轮测速才会重选。验证时要看最终选中的是哪个节点,而不是只看「是否处于自动模式」这个标志位。

另外三个值得提前处理的事项

← 左右滑动查看完整表格 →

事项风险建议
开机自启关闭重启后内核不会自动启动,但系统代理仍指向本地端口,所有应用都连不上,表现为「完全没网」。要么打开开机自启,要么不用时把系统代理一并关掉,不要留半开状态。
订阅自动更新自动更新可能把分组选择、自定义规则等改动覆盖回默认值。改完配置后,等一轮自动更新再复查一次,确认没被覆盖。
换网络环境新接口不会自动继承已有接口的代理设置,这是本文问题的根因。换了网络(插网线、连热点、手机共享)后,先查一次系统代理状态。

小结

代理「看起来在运行但其实没生效」,按这个顺序查:

① 系统代理是否真的启用了(不是看软件界面,是看系统快照)→ ② 当前在用的网络服务有没有被写入(系统代理按接口分别保存)→ ③ 内核端口是否监听、手动指定代理能否出网 → ④ 规则分流是否符合预期(国内直连、境外出网、接口有业务响应)。

最值得记住的一条:命令行手动指定代理能通,不代表浏览器也能通。两者之间隔着「系统代理」这一层,而这一层是按网络服务逐个写入的,换网络就会露出空白。下次遇到「代理开着但上不了网」,先跑一次系统代理快照,通常一眼就能看出问题。

ToDesk 远程连不上、黑屏、采集画面异常:双 Service 冲突的定位与修复
macOS 上 ToDesk 黑屏、采集画面异常、连进来就锁屏——从三进程结构与四层排查入手,定位双 Service 冲突与本地 IPC 断开