macOS 上 Clash 系统代理不生效:浏览器与终端分开排查

Safari、Chrome 走系统代理,终端却默认不走。本文分两条线定位:先查网络设置里的代理字段,再为 shell 补 http_proxy 环境变量。

先区分系统代理、终端代理与 TUN

Clash 客户端显示“系统代理已开启”,表示客户端尝试向 macOS 写入代理设置,不代表所有应用流量都已接管。Safari 通常使用系统代理;Chrome 默认也会读取系统代理,但扩展、启动参数或组织策略可以改变其行为。终端中的命令行程序则各有自己的代理入口,不能用浏览器是否能打开网页来推断。

图形客户端负责导入配置、切换策略组和管理系统设置;内核负责监听代理端口、匹配规则并转发请求。客户端界面仍在运行时,内核也可能因为配置错误而没有成功启动。排查时应把“代理设置已写入”和“本地端口确实可用”分开验证。

接入方式主要检查位置作用边界
系统代理macOS 当前网络服务的代理字段应用需要主动遵循这些设置
终端显式代理命令参数、环境变量、工具配置通常只影响指定进程及其子进程
TUN 模式虚拟网卡、路由、DNS 与权限依赖客户端与内核支持,是另一条接入路径

确认内核运行与本地监听端口

读取运行配置,而不是只看订阅文件

先在客户端查看当前配置是否加载成功,以及 HTTP、SOCKS 或混合端口的实际数值。不同客户端的界面名称和覆写方式并不相同,不能套用一条统一的菜单路径。订阅文件里的端口还可能被客户端设置覆写,因此应优先看运行配置和启动日志。

支持混合端口的内核可以用同一端口接收 HTTP 与 SOCKS 请求。下面是局部字段示例,不是一份包含节点和策略组的完整订阅,也不需要为了排查而覆盖原文件:

mixed-port: 7890
allow-lan: false
mode: rule

allow-lan: false 不妨碍本机使用 127.0.0.1。只排查当前 Mac 时,不需要为了让系统代理生效而开放局域网访问。检查 TCP 监听可运行:

lsof -nP -iTCP:7890 -sTCP:LISTEN
  • 出现监听记录:检查进程是否属于预期客户端或其内核,并确认监听地址允许本机回环连接。
  • 没有记录:检查端口是否记错、内核是否启动失败,以及配置日志中的解析错误。必要时再用管理员权限复查进程可见性。
  • 端口被其他程序占用:先处理冲突,再同步修改客户端监听端口与系统代理端口,不要只改其中一处。

绕过系统设置,直接验证代理入口

curl -I -v --connect-timeout 10 --max-time 20 \
  --noproxy "" \
  --proxy http://127.0.0.1:7890 \
  https://example.com/

此命令通过明确指定的 HTTP 代理访问 HTTPS 地址;--noproxy "" 用于排除当前绕过列表的影响。example.com 只是连通性测试目标,也应替换成实际出问题的网站复测。响应状态码并非必须为 200,重点是区分本地连接失败、代理隧道失败、TLS 错误与远端 HTTP 响应。

如果提示无法连接 127.0.0.1:7890,先修复本地监听;如果已连接到本地代理后超时,再看客户端连接记录、规则命中和节点日志。显式代理能够访问,而浏览器仍失败时,才更值得检查 macOS 的代理写入路径。

浏览器不生效:检查当前网络服务的代理字段

确认正在使用的 Wi-Fi 或以太网服务

在采用“系统设置”界面的 macOS 中,进入「系统设置」→「网络」→ 当前使用的网络服务 →「详细信息」→「代理」。Wi-Fi 也可从「系统设置」→「Wi-Fi」→ 当前网络的「详细信息」进入。旧版系统偏好设置通常位于「网络」→ 当前服务 →「高级」→「代理」。以本机界面显示为准。

检查“网页代理(HTTP)”与“安全网页代理(HTTPS)”:服务器应指向 127.0.0.1,端口应与客户端的 HTTP 或混合端口一致。这里的 HTTPS 代理设置是供 HTTPS 目标请求使用,不表示本地代理地址必须写成 https://127.0.0.1。

macOS 的代理设置按网络服务保存。刚从 Wi-Fi 切换到 USB 以太网时,旧服务上的正确设置不等于新服务也正确。先列出服务,再查询当前使用的那一项:

networksetup -listallnetworkservices
networksetup -getwebproxy "Wi-Fi"
networksetup -getsecurewebproxy "Wi-Fi"
networksetup -getproxybypassdomains "Wi-Fi"
networksetup -getautoproxyurl "Wi-Fi"
networksetup -getproxyautodiscovery "Wi-Fi"

如果服务名不是 Wi-Fi,用第一条命令列出的实际名称替换,并保留引号。下面只是预期字段示例,不是本机检测结果;HTTP 与 HTTPS 两项都应分别检查:

Enabled: Yes
Server: 127.0.0.1
Port: 7890
Authenticated Proxy Enabled: 0

排除 PAC、绕过列表与浏览器覆盖

  • 自动代理配置:PAC 脚本可以按目标返回代理或直连。记录原值后,临时只保留正在验证的一种代理方式,避免把 PAC 的分流结果误判为手动代理失效。
  • 绕过列表:localhost、127.0.0.1、*.local 常用于本地访问。检查是否误加了目标域名或过宽的通配规则,不必清空所有本地绕过项。
  • 浏览器扩展:在 Chrome 暂停代理切换扩展后复测;受组织管理的设备还需检查策略,不能直接把受管设置覆盖掉。
  • 多个客户端:避免同时让两个代理工具写入系统设置。设置反复跳回旧端口时,先确定哪个进程仍在接管。

修改后重新发起网页请求,必要时完全退出再打开浏览器。优先选择一个此前未加载的页面,避免缓存让结果失真。与此同时观察客户端的连接记录:完全没有新连接,倾向于应用未进入代理;出现连接却被规则送往 DIRECT,则属于规则选择问题。

浏览器正常、终端失败:设置 shell 代理变量

在终端中启动的 curl、Git、包管理器并不统一遵循 macOS 系统代理。部分工具读取环境变量,部分工具读取自己的配置,还有些使用系统网络 API。应先对单个命令做临时测试,再决定是否配置整个 shell。

先对单条命令设置代理

http_proxy=http://127.0.0.1:7890 \
https_proxy=http://127.0.0.1:7890 \
curl -I --connect-timeout 10 --max-time 20 https://example.com/

这里的 https_proxy 仍使用 http://,因为它描述的是本地 HTTP 代理入口。访问 HTTPS 站点时,工具通常通过 HTTP CONNECT 建立隧道。不要仅因目标是 HTTPS 就把变量值改成 TLS 代理地址。

为当前终端会话导出变量

export http_proxy="http://127.0.0.1:7890"
export https_proxy="http://127.0.0.1:7890"
export no_proxy="localhost,127.0.0.1,::1"

env | grep -iE '^(http|https|all|no)_proxy='
curl -I --connect-timeout 10 --max-time 20 https://example.com/

这些导出只影响当前 shell 和之后启动的子进程,不会自动改变其他已打开的终端窗口,也不会覆盖所有从 Dock 启动的图形应用。不同工具对变量大小写的支持不同;例如 curl 的 HTTP 代理变量采用小写 http_proxy,不应只设置大写 HTTP_PROXY。

若使用 SOCKS 入口,可以单独测试下面的命令。只有端口确实支持 SOCKS 时才使用;本例混合端口支持两类请求。socks5h 让目标主机名交给代理端解析,有助于区分本机 DNS 解析与代理路径的问题。

curl -I --noproxy "" \
  --proxy socks5h://127.0.0.1:7890 \
  --connect-timeout 10 --max-time 20 \
  https://example.com/

持久化前先处理旧变量

确认临时测试通过后,交互式 zsh 可将需要的 export 行加入 ~/.zshrc,再打开新窗口或执行 source ~/.zshrc。其他 shell 应使用对应的启动文件。客户端关闭后,这些变量仍会指向本地端口,因此按需启用通常比永久开启更容易排错。

# 撤销当前 shell 的常见代理变量
unset http_proxy https_proxy all_proxy no_proxy
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY

如果变量写入了启动文件,还需删除或注释对应行,否则新窗口会再次加载。检查 all_proxy 是否指向旧端口,以及 no_proxy 是否包含目标域名;不同程序的优先级与匹配语法可能不同,不宜同时堆叠多套代理变量。

只有 Git 或包管理器失败:检查工具级配置

当相同终端里的 curl 已经通过,而 Git 仍失败,应继续查看 Git 的配置来源,而不是反复切换系统代理。旧的全局代理、仓库局部代理或按 URL 设置的代理,都可能让 Git 使用另一条路径。

git config --show-origin --get-regexp '.*proxy.*'

# 只对本次 HTTPS 仓库访问指定代理
git -c http.proxy=http://127.0.0.1:7890 \
  ls-remote https://github.com/git/git.git HEAD

第二条命令是一次公开仓库的连通性测试,不会修改全局配置。若查询输出包含账号、密码或私人地址,分享日志前先脱敏。确实确认是错误的全局 http.proxy 时,再使用 git config --global --unset http.proxy;仓库局部和 URL 专用配置则应在对应作用域处理。

  • 使用 HTTPS 地址的 Git 请求与 git@主机:仓库 形式的 SSH 请求是两套协议入口,不能用 HTTP 代理测试结果直接推断 SSH。
  • npm 可通过 npm config get proxy 和 npm config get https-proxy 检查独立设置,修改前先记录原值。
  • 使用 sudo 后,环境变量可能被清理。不要为了传递代理而直接保留所有环境变量,应查该工具所需的最小配置。

请求已经进入 Clash:检查规则、DNS 与 TUN

连接记录中出现目标请求,说明至少这一条请求已经到达内核。此时“系统代理不生效”往往不是准确描述,需要查看命中的规则、策略组当前选择以及最终出口。规则模式通常按配置顺序匹配,末尾的 MATCH 只处理前面未命中的请求;选中某个策略组也不保证它的当前成员就是代理节点。

  1. 规则出口:记录目标域名命中的规则以及最终是节点、DIRECT 还是 REJECT。配置了直连规则时,请求经过内核后仍然可以直连。
  2. 节点与线路:同一个目标换一个可用节点复测。客户端内的延迟测试只代表测试地址的结果,不能代替目标网站的实际连接。
  3. DNS:区分本机解析失败、内核上游解析失败和远端连接失败。不要把开启 fake-ip 当作通用修复;它还涉及 DNS 接管与应用兼容性。
  4. TUN:只有确实需要接管不遵循显式代理的应用时,再检查客户端支持、权限、路由与 DNS 配置。基于 mihomo 的客户端也可能提供不同的 TUN 控制项,不能混用其他客户端的界面步骤。

复测顺序与退出后的恢复

完成调整后,按下面顺序验证。浏览器和终端分别成功,才能说明两条接入路径都配置正确;单个网页打开并不能证明所有应用都会通过代理。

  1. 用 lsof 确认实际端口处于监听状态。
  2. 用 curl --proxy 显式测试,确认本地入口与目标访问均可用。
  3. 检查当前网络服务的 HTTP 与 HTTPS 字段,再在浏览器重新请求目标页面。
  4. 清点终端变量与工具级代理,在同一终端分别测试 curl 和实际业务命令。
  5. 退出客户端前关闭其系统代理接管;退出后再检查网络设置,避免代理仍指向已停止监听的端口。

如果客户端异常退出后所有网页都无法访问,进入当前网络服务的「代理」页面,记录并关闭残留的本地手动代理,随后撤销终端变量再测试直连。受组织管理的代理与 PAC 应按管理要求恢复,不要一并删除。仍需定位时,整理实际端口、服务名、显式代理测试的错误阶段和脱敏连接日志,比仅描述“代理没反应”更有帮助。

端口与规则字段可继续查阅 YAML 手册;配置导入与 TUN 的平台操作可参照 使用教程。排查核心始终是把应用入口、本地内核和远端出口逐段分开。

下载Clash