macOS 终端环境搭建:为什么下载依赖时请先关闭 VPN/代理

背景:一个看似矛盾的现象

在 macOS 上搭建开发环境(如安装 Homebrew、Node.js、Python、Git 仓库等)时,我们几乎离不开终端。与此同时,很多开发者习惯保持 VPN 或代理(如 ClashX、Surge、V2Ray 等)开启,以便在浏览器中顺畅访问 GitHub、Stack Overflow 等境外网站。

然而,正是在这种“双开”状态下,终端里的下载命令却频频报错:

  • brew install 卡在 Updating Homebrew... 半天不动
  • git clone 抛出 Connection refused443 错误
  • npm install 出现 request to https://registry.npmjs.org/... failed, reason: connect ETIMEDOUT
  • 甚至拉取国内镜像源也报 certificate has expired 之类的 SSL 错误

一旦关闭代理,这些错误常常立刻消失。这背后的原因究竟是什么?终端环境下的流量与浏览器到底有什么不同?

为什么终端下载不应该盲目开启代理

1. 终端不会自动继承系统代理

macOS 的 系统代理设置(在“网络偏好设置”中配置的 HTTP/HTTPS 代理)主要对符合 Apple 网络框架 的应用生效——比如 Safari、App Store,甚至部分 Chrome 功能,但绝大多数命令行工具(curlwgetgitbrewnpmpip 等)并不遵循这一设置。

终端程序要使用代理,通常需要依赖环境变量:

  • http_proxy / HTTP_PROXY
  • https_proxy / HTTPS_PROXY
  • all_proxy / ALL_PROXY(socks5 等)

如果你只是开启了 ClashX 或 Surge 的“系统代理”开关,而没有在终端里额外设置这些环境变量,那么终端流量其实是直连的,表面上看起来代理对它毫无影响,对吧?问题恰恰出在另一个常见操作上:很多代理工具提供“增强模式”或“终端代理”功能,会自动注入或修改这些环境变量。当规则配置不适当时,反而会导致大量本该直连的流量被强行拉入代理,酿成故障。

2. 代理规则的“无差别误伤”

浏览器访问网页时,代理工具通常根据域名、IP 等规则进行智能分流:国内网站直连,国外网站走代理。但终端里访问的地址经常混杂着:

  • 国内镜像源(如清华、中科大、阿里云的 Homebrew 镜像)
  • 企业内部私有仓库(内网 npm、私有 Git 服务器)
  • 云服务商的内网地址(如 oss-cn-beijing.aliyuncs.com
  • 本机服务localhost127.0.0.1

若代理规则未正确排除这些地址,终端里的请求就会被错误地发往代理服务器。代理服务器要么不认识这些内网地址,要么因为网络隔离而连接超时,表现为各种“超时”“拒绝连接”错误。

3. HTTPS 证书校验冲突

部分代理工具会对 HTTPS 流量进行 中间人解密(MITM),以便审查或修改内容。这需要代理在本地生成并安装自签名证书。浏览器通常由于导入了该证书而信任它,但终端里的工具(curlgitpython 等)使用系统独立的证书存储,不一定认这个“外来证书”。

于是你会看到类似错误:

1
2
SSL certificate problem: unable to get local issuer certificate
fatal: unable to access 'https://github.com/...': SSL certificate problem

即使你关闭了 MITM,某些代理在处理 HTTPS 流量时,仍可能破坏 TLS 握手(例如不支持 SNI、ALPN 协商),导致 ssl3_get_record 错误。

4. 协议与认证方式的限制

很多下载场景使用 SSH 协议(如 git clone git@github.com:user/repo.git)或 非标准端口。普通 HTTP(S) 代理无法直接承载这些流量,需要额外的封装(如 ssh over httpssocks5)。若环境变量只配置了 HTTP 代理,SSH 连接便会直接失败,而错误信息往往不够直观,让人摸不着头脑。

5. 环境变量的“污染”与遗漏

有些开发者为了在终端临时翻墙,会在 ~/.zshrc~/.bash_profile 里添加:

1
2
export http_proxy=http://127.0.0.1:7890
export https_proxy=http://127.0.0.1:7890

这样虽一劳永逸,但时间一长就忘了这回事。当代理软件没启动(端口不通)或者切换了网络环境后,所有终端网络请求都会因为连不上本地代理端口而全部瘫痪,并且很难第一时间想到是环境变量在作怪。

终端下载时的正确处理方式与注意点

1. 检查当前终端的代理状态

在下载出错时,第一时间执行:

1
env | grep -i proxy

如果输出类似 http_proxy=http://127.0.0.1:7890,说明当前终端已配置代理。你也可以用:

1
echo $http_proxy

来确认。若发现有值且你目前不需要代理,可以直接清除(见下一条)。

2. 临时关闭终端代理

在当前终端会话中执行:

1
unset http_proxy https_proxy all_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY

这只会影响当前窗口,关闭窗口或新建标签页后,会重新加载 shell 配置文件。若你想永久关闭,请检查并注释掉 ~/.zshrc~/.bash_profile 中相关 export 语句,然后执行 source ~/.zshrc 使其生效。

3. 关闭代理工具的“增强/终端代理”模式

以 ClashX 为例:

  • 关闭 Enhanced Mode(增强模式)
  • 或在“设置”中取消勾选 Set as system proxy 的同时,留意是否有“终端代理”相关开关(某些版本会额外注入环境变量)
  • Surge 用户可检查 Set as System ProxyEnhanced Mode 两个选项

如果你的代理工具仅用于浏览器,建议停用所有针对系统范围或终端的功能,避免干扰命令行。

4. 为单个命令按需配置代理(而不是全局写入)

如果你确实需要让某个命令走代理,建议不要修改 shell 全局配置,而是使用命令前缀的方式:

1
2
# 仅此次 git clone 走代理
http_proxy=http://127.0.0.1:7890 https_proxy=http://127.0.0.1:7890 git clone https://github.com/example/repo.git

或使用更为精细的 tool 级配置:

1
2
3
4
5
6
7
# 为 git 设置代理(仅针对 HTTP 协议)
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890

# 不需要时删除配置
git config --global --unset http.proxy
git config --global --unset https.proxy

这样就不会污染整个终端环境,其他不受影响的命令依然直连。

5. 优先使用国内镜像源,减少代理依赖

很多下载问题其实不必依赖代理,通过换用高速国内镜像源即可解决:

  • Homebrew:推荐使用中科大、清华镜像(见相应教程)
  • npm:设置淘宝源 npm config set registry https://registry.npmmirror.com
  • pip:使用阿里云/清华源 pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
  • Docker:配置国内镜像加速器

这样做既加快了下载速度,又规避了终端代理引发的一连串问题。

6. 内网地址务必加入代理绕过列表

如果你必须长期在终端中保留代理设置,请确保将内网、本机地址写入 no_proxy 环境变量(多数命令行工具都支持):

1
export no_proxy="localhost,127.0.0.1,::1,*.local,*.internal,192.168.*,10.*,172.16.*"

这样可以避免访问公司内部 GitLab、私有 npm registry 或后端服务时被代理干扰。

总结

在 macOS 下搭建开发环境、使用终端下载软件包时,不要想当然地认为“开了 VPN 就能加速”。终端网络行为与浏览器截然不同,代理配置稍有不慎就会让一个简单的 brew install 变成卡关噩梦。

一套安全、高效的习惯是:

  1. 环境搭建阶段,先关闭终端代理(清除环境变量、关闭代理工具增强模式);
  2. 使用国内镜像源改善直连速度;
  3. 若个别资源确实需要代理,仅在执行该命令时临时注入环境变量,用完即焚;
  4. 定期检查 env | grep -i proxy,避免遗留配置影响后续工作。

谨记这些要点,你将免受大量莫名其妙的网络问题困扰,把精力真正留在写代码上。