搜索 K
Appearance
如果你只想知道答案,这里是三句话版本:
scutil --proxy 那份配置,除非程序自己实现了代理读取逻辑(比如 curl、git、npm 这些显式读环境变量的工具)。proxy on / proxy off 一键切换环境变量,遇到不读环境变量的二进制程序(比如某些 Rust/Go 工具、AOSP 工具链、部分容器运行时),再用 Clash Verge Rev / Mihomo Party 的 TUN 模式全局接管。all_proxy 会导致 kubectl、docker build、本地 localhost 调试、内网 SSO 回调集体翻车。代理是工具,不是常驻状态。为什么值得花一整篇长文讲这件事?因为在真实的开发场景里,终端代理出问题带来的损耗远超想象:一次 git clone 卡死 8 分钟、brew install 反复超时、docker pull 走错链路白烧 3 个 G、npm install 拉了一半断流导致 node_modules 脏了还得重装。这些都不是「网速慢」三个字能解释的,背后是代理链路、DNS 解析路径、TLS 握手位置、拥塞控制算法四层耦合的结果。
这篇文章会把这四层拆开,给到可直接复制粘贴的配置,也给到能定位问题的命令。
macOS 上有两套代理配置体系,很多人的认知盲区就在这里:
第一套是 System Configuration 框架层。 你在「系统设置 → 网络 → 详细信息 → 代理」里勾选的 HTTP/HTTPS/SOCKS 代理,会被写入 preferences.plist,由 configd 守护进程广播。任何使用 CFNetwork / NSURLSession / WebKit 的 App(Safari、Chrome、大多数原生 App)会自动继承这套配置。你可以用下面这条命令查看当前系统级代理:
scutil --proxy输出的 HTTPEnable、HTTPSProxy、SOCKSEnable 等字段,就是 CFNetwork 用的那一份。
第二套是 POSIX 环境变量层。 终端里跑的绝大多数 CLI 工具——curl、wget、git、npm、pip、cargo、go——读的是 http_proxy / https_proxy / all_proxy / no_proxy 这一组环境变量。这组变量默认是空的,macOS 不会因为你在系统设置里勾了代理就自动注入它们。
这就是「浏览器能上网、终端连不上」的根本原因:它们根本不在读同一份配置。
当你在 iTerm2 里敲下 git clone https://github.com/xxx,内核层面发生的事是这样的:
git 进程 → getaddrinfo("github.com") → DNS 查询(走 /etc/resolv.conf)
→ connect(93.184.x.x:443) → 查路由表 → 匹配默认网关
→ 从 en0 发出 TCP SYN → 运营商网络 → ...关键在于 connect() 这一步。如果没有任何代理机制介入,这个 SYN 包会带着你本机真实的源 IP 直接发往目标地址。目标地址如果是被墙的 IP 段,SYN 会被 RST 或者直接黑洞丢弃,表现就是「卡住不动,最后 timeout」。
要让这个包「绕道」,只有三种可能在链路层面生效的机制:
127.0.0.1:7890 发起 CONNECT 请求,把目标地址交给代理服务器。utun3,修改路由表把默认路由指向它,内核态的代理程序(mihomo / sing-box 内核)接管所有 IP 包。connect() 系统调用,在用户态改写目标地址。理解这三点,后面所有的配置和排障都会变得清晰。
终端场景和流媒体场景对线路的敏感度完全不同。
流媒体主要吃带宽和解锁能力,卡顿容忍度高;而终端场景主要吃延迟稳定性和小包往返效率。git clone 一个仓库会触发几百上千次 TLS 往返、大量并发的 HEAD 请求、以及 TLS 1.3 的 session resumption,任何一次 RTT 抖动都会被放大成整体耗时。
这就引出了线路类型的量化差异:
| 线路类型 | 典型 RTT(上海→洛杉矶) | 晚高峰抖动 | 丢包率 | 终端 clone 提速倍数 | 适用场景 |
|---|---|---|---|---|---|
| 裸连直出 | 250-400ms+ | ±100ms | 5%-30% | 1x(基准) | 基本不可用 |
| 公网 BGP 中转 | 180-260ms | ±40ms | 0.5%-3% | 3x-8x | 轻量拉包、临时使用 |
| 双 ISP 家宽中转 | 160-220ms | ±10ms | 0.1%-0.5% | 8x-20x | 性价比优先 |
| IPLC 国际专线 | 150-190ms | ±5ms | 低于 0.1% | 10x-25x | 稳定性优先 |
| IEPL 企业级专线 | 150-185ms | ±3ms | 低于 0.05% | 12x-30x | 团队协作 / CI |
这里面的技术差异来自三层:
第一层是物理路径。 IPLC/IEPL 走的是运营商之间的私有租用线路,不经过公网 BGP 的 AS 跳转,天然规避了国际出口的拥塞队列。BGP 中转则要经过 8-15 跳 AS,每一跳都可能存在 QoS 限速策略。
第二层是拥塞控制。 服务端如果启用了 BBRv3,在高丢包环境下能显著提升吞吐——BBRv3 相比 BBRv1 增加了对 ECN 的利用和对 ACK 聚合的建模,在 1%-3% 丢包时吞吐衰减明显小于 CUBIC。但 BBR 的效果依赖于服务端内核版本,客户端侧改不了。
第三层是 TLS 握手位置。 如果代理服务端做的是纯 TCP 转发(Reality / Trojan),TLS 握手发生在你本地和目标服务器之间,握手 RTT 直接叠加在你的链路延迟上。如果服务端做了 MITM 式的中间人优化(部分企业级 IEPL 会做 TLS 会话预建),首次握手 RTT 可以被压缩 1 个来回。
这也是为什么在做终端选型时,「低抖动」比「高带宽」重要得多。一个 ±3ms 抖动的 100M 专线,在 npm install 这种高频小包场景下,体验会明显优于一个 ±40ms 抖动的 500M 公网中转。
下面这张表是我在实际项目和团队部署中反复验证过的结果,指标取值为 MacBook Pro M3 + 千兆家宽 + 晚高峰(20:00-23:00)多次测量的中位数。
| 方案 | 生效范围 | 额外延迟 | 吞吐损耗 | UDP/QUIC | DNS 防污染 | 配置复杂度 | 对本地服务影响 | 内存占用 | 推荐指数 |
|---|---|---|---|---|---|---|---|---|---|
| 环境变量(http_proxy) | 仅显式读变量的程序 | 0-1ms | 5%-12% | 不支持 | 依赖代理端解析 | 1/5 | 无 | 可忽略 | ⭐⭐⭐⭐⭐ |
| zsh 函数别名 | 同上,可一键开关 | 0ms | 同上 | 不支持 | 同上 | 1/5 | 无 | 可忽略 | ⭐⭐⭐⭐⭐ |
| TUN 模式全局接管 | 全系统所有进程 | 3-15ms | 15%-35% | 支持 | 强制走代理解析 | 3/5 | 需配置 bypass 列表 | 100-400MB | ⭐⭐⭐⭐ |
| Proxychains-ng | LD_PRELOAD 加载的程序 | 5-20ms | 20%-40% | 部分支持 | 需手动配置 | 4/5 | 部分程序崩溃 | 低 | ⭐⭐⭐ |
| SSH ProxyCommand | ssh / scp / rsync | 2-8ms | 不适用 | 不支持 | 走远端 DNS | 3/5 | 无 | 低 | ⭐⭐⭐⭐ |
| 旁路由透明代理 | 局域网全部设备 | 2-10ms | 10%-30% | 支持 | 支持 | 5/5 | 不适用 | 不适用 | ⭐⭐⭐⭐ |
| 容器独立代理 | 容器内进程 | 同环境变量 | 同环境变量 | 取决于配置 | 取决于配置 | 3/5 | 无 | 低 | ⭐⭐⭐ |
几个关键结论:
docker pull 这类大流量任务时依然能感知到。LD_PRELOAD 劫持 connect(),对使用 musl libc 静态链接的二进制(很多 Go 程序)完全无效,对启用了 seccomp 的程序可能导致段错误。主战场是 npm、pnpm、yarn、vite、playwright。这类工具的特点是并发连接数极高(pnpm 默认并发 16,playwright 下载浏览器可能开几十个连接)。
HTTP_PROXY 大小写敏感,必须同时设置大写和小写两套。Playwright 下载浏览器走的是 CDN,建议单独配 PLAYWRIGHT_DOWNLOAD_HOST 走国内镜像。主战场是 kubectl、terraform、aws-cli、docker、helm、ansible。
kubectl 读 HTTPS_PROXY,但不读 ALL_PROXY。terraform 的 provider 下载走 HTTP_PROXY,但 state 后端如果在内网必须放行。terraform apply 中途断流会造成 state 锁残留,处理起来非常痛苦。主战场是 pip、conda、huggingface-cli、git-lfs、wget 大文件。
aria2c 多线程。hf_transfer 能显著提升 HuggingFace 下载速度。git-lfs 需要单独配置 lfs.http:// 相关项,否则 LFS 指针能拉下来但大文件拉不下来。在咖啡馆、酒店、共享办公空间频繁切换网络。
如果你的终端工作流涉及频繁的 CI/CD 触发、多仓库并行 clone、大模型权重拉取,那么线路的稳定性权重远高于峰值带宽。以【隐形人】这类采用企业级 IEPL 专线的服务为例,其 60+ 原生机房独立 IP 和 500M 冗余带宽的设计,本质上是在解决「晚高峰公网出口拥塞」这个痛点——这也是为什么同样的配置脚本,在不同线路上的体感差异能到 3-5 倍。选择时可参考 /reviews/invisible/ 的实测数据,重点看抖动和丢包两列而不是峰值速度。
把下面这段加到 ~/.zshrc 末尾。建议把端口变量提取到顶部,方便日后换客户端时只改一处。
# ===== 代理开关配置 =====
PROXY_HOST="127.0.0.1"
PROXY_HTTP_PORT="7890"
PROXY_SOCKS_PORT="7891"
PROXY_NO_PROXY="localhost,127.0.0.1,::1,*.local,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,*.internal,*.corp,*.aliyuncs.com"
proxy_on() {
export http_proxy="http://${PROXY_HOST}:${PROXY_HTTP_PORT}"
export https_proxy="http://${PROXY_HOST}:${PROXY_HTTP_PORT}"
export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$https_proxy"
export all_proxy="socks5://${PROXY_HOST}:${PROXY_SOCKS_PORT}"
export ALL_PROXY="$all_proxy"
export no_proxy="$PROXY_NO_PROXY"
export NO_PROXY="$PROXY_NO_PROXY"
echo "✅ 代理已开启 → http://${PROXY_HOST}:${PROXY_HTTP_PORT}"
}
proxy_off() {
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
unset all_proxy ALL_PROXY no_proxy NO_PROXY
echo "⛔ 代理已关闭"
}
proxy_status() {
if [ -n "$http_proxy" ]; then
echo "当前代理:$http_proxy"
curl -s --max-time 5 https://api.ip.sb/ip || echo "⚠️ 代理连通性测试失败"
else
echo "当前未开启代理"
fi
}为什么 no_proxy 里要包含 *.aliyuncs.com? 因为很多国内开发者的 npm registry、docker registry、Maven 仓库都在阿里云上,走代理反而绕远且容易触发风控。同理,如果你用飞书、企业微信的 Webhook,也应该加进白名单。
如果你会同时用多个客户端(比如家里用 Clash Verge Rev,公司用 Surge),端口可能不同。加一个自动探测:
detect_proxy_port() {
for