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 或重开。
  • 端口写死;换端口只改 ~/.zshrcPROXY_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 开关是”该工具整体绕过”,按需选用。

相关