macOS 终端代理配置排错
现象:本机网络很慢,尤其终端里的下载(npm / brew / pip / git / curl),感觉”很多下载没走代理”。
结论(一句话)
代理本身是好的,问题是终端里没有稳定地拿到代理变量——变量只被临时注入到个别进程,没写进配置文件,新开的终端窗口一个代理变量都没有,于是那些下载全部直连;而本机直连基本不通,所以表现为”卡到超时”,而不是报错。
根因拆解
| 现象 | 原因 |
|---|---|
| 很多下载没走代理 | 代理变量没写进任何 rc 文件,只是当前进程被临时注入。新开终端窗口里没有任何代理变量 |
| 没代理时”特别慢” | 本机直连基本不通(直连 api.ipify.org 直接超时),漏代理的工具会卡到超时,而非快速报错 |
| 部分工具漏代理 | 就算注入了也只有大写 HTTP_PROXY;curl 处理 HTTP、部分 node/python 库只认小写 http_proxy,大写不读 |
| git / npm / pip 漏代理 | 它们没有自己的持久代理配置,全靠环境变量,而环境变量在新窗口里是空的 |
关键知识点
- macOS 系统代理(GUI)和终端 CLI 是两套东西。系统设置 / Clash 的”系统代理”开关,通常只对走系统网络栈的应用(浏览器等)生效;
npm / brew / pip / git / curl / wget默认只认环境变量http_proxy/https_proxy。 - 大小写都要设。
curl处理 HTTP 只读小写http_proxy;部分库只认小写。稳妥做法是大写小写、外加all_proxy(socks)全设一遍。 no_proxy要带上本地地址(localhost,127.0.0.1,::1,.local),避免本地请求也绕代理。- 排查代理是否生效的最快办法:对比出口 IP
curl -s https://api.ipify.org # 默认(读 env),应为代理节点 IP curl -s --noproxy '*' https://api.ipify.org # 强制直连做对照
解决方案:智能代理(能走代理走代理,否则直连)
需求:代理开着就走代理,代理关了就直连(不要卡超时)。做法是在 zsh 启动时用内建 ztcp 探测本地代理端口(纯内建、无外部依赖、本地探测瞬时返回,不拖慢开终端),在监听就导出全套代理变量,否则什么都不设。
写进 ~/.zshrc(端口按自己客户端固定的值改 PROXY_PORT):
# ============================================================
# 网络代理(智能:本地代理端口在监听就走代理,否则直连)
# ============================================================
PROXY_ADDR="127.0.0.1"
PROXY_PORT="39848" # 改成自己客户端固定的混合端口
# 探测本地端口是否在监听(zsh 内建 ztcp,本地瞬时返回)
_proxy_port_up(){
zmodload zsh/net/tcp 2>/dev/null || return 1
ztcp "$PROXY_ADDR" "$PROXY_PORT" 2>/dev/null || return 1
ztcp -c "$REPLY" 2>/dev/null
return 0
}
_proxy_set(){
export http_proxy="http://$PROXY_ADDR:$PROXY_PORT"
export https_proxy="$http_proxy"
export all_proxy="socks5://$PROXY_ADDR:$PROXY_PORT"
export HTTP_PROXY="$http_proxy" HTTPS_PROXY="$https_proxy" ALL_PROXY="$all_proxy"
export no_proxy="localhost,127.0.0.1,::1,.local"
export NO_PROXY="$no_proxy"
}
_proxy_unset(){
unset http_proxy https_proxy all_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY
}
# 开终端时自动判定
if _proxy_port_up; then _proxy_set; fi
# 手动开关
proxyon(){ _proxy_set; echo "✅ proxy ON → $http_proxy"; }
proxyoff(){ _proxy_unset; echo "⛔ proxy OFF(直连)"; }
proxystatus(){
if [ -n "$http_proxy" ]; then echo "proxy ON → $http_proxy"
else echo "proxy OFF(直连)"; fi
}用法
| 命令 | 作用 |
|---|---|
| (开新终端) | 自动探测:代理开着走代理,没开直连,不会卡超时 |
proxyon | 手动强制开代理 |
proxyoff | 临时直连(代理节点抽风时) |
proxystatus | 查看当前状态 |
source ~/.zshrc | 当前已开的终端里立刻生效 |
注意点
- 只影响新开的终端。已经开着的窗口要
source ~/.zshrc或重开。 - 端口写死;换端口只改
~/.zshrc里PROXY_PORT一个值。 - 若客户端每次启动随机换端口,把混合端口在客户端里固定下来,否则写死会失效。
国内服务应绕过代理(重要)
代理节点在国外。国内服务(飞书、Gitee、阿里云、国内 npm 镜像等)不该走代理——绕一圈国外反而更慢,凭证也多经一层。有两种绕过方式:
1. 工具自带开关
有些 CLI 检测到代理会主动提醒。例如 lark-cli:
[lark-cli] [WARN] proxy detected: HTTPS_PROXY=... — requests (including credentials) will transit through this proxy.
Set LARK_CLI_NO_PROXY=1 to disable proxy, or LARK_CLI_NO_PROXY_WARN=1 to keep the proxy and silence this warning.
正确做法是让它绕过代理直连(而不是只静音):
export LARK_CLI_NO_PROXY=1 # lark-cli 直连飞书;已加入 ~/.zshrc
LARK_CLI_NO_PROXY_WARN=1只是关掉提醒、流量仍走代理,不推荐。
2. 用 no_proxy 排除国内域名
通用做法:把国内域名加进 no_proxy,所有认这个变量的工具都会对这些域名直连。
export no_proxy="localhost,127.0.0.1,::1,.local,.cn,.feishu.cn,.aliyun.com,npmmirror.com"
export NO_PROXY="$no_proxy"注:
no_proxy是”按域名/网段绕过”,而工具自带的*_NO_PROXY开关是”该工具整体绕过”,按需选用。