中文 English

诡异!Tmux 新 session 里凭空多出一个 http_proxy 环境变量

发布时间: 2026-05-30
tmux zsh environment proxy debugging macos

前言

今天遇到一个非常诡异的问题:新创建的 tmux session 里,echo $http_proxy 竟然显示了一个有效的代理地址。但我明明记得自己在 .zshrc 里只定义了两个 alias:proxy_onproxy_off,它们都是手动触发的,不会自动执行。

诡异的是,我的主 shell 里 http_proxy明明是空的,但一进 tmux 就有了。这个问题排查了将近一个小时才找到根因,特此记录。

问题现象

主 shell(Terminal.app / iTerm2):

$ echo $http_proxy

# 空!什么都没有

新建 tmux session:

$ tmux new -s test
$ echo $http_proxy
http_proxy=http://x.x.x.x:1081/

注意,这里出现了一个内网 IP 地址段的代理,而且端口 1081 和我平时用的 1031 不一样。这说明这个值不是我自己配置的。

问题对比:主 Shell vs Tmux Session


问题分析

第一步:检查配置文件

我先检查了所有常见的配置文件:

结果:配置文件里没有任何地方会自动设置 http_proxy

排查步骤

第二步:检查外部工具注入

我发现 .codex/service-env/ai.codex-desktop.env 文件内容为:

http_proxy=http://x.x.x.x:1081
https_proxy=http://x.x.x.x:1081

这个文件是 Codex 桌面应用通过 launchd 注入的环境变量。但这个文件不应该被 tmux/zsh 自动加载。

第三步:关键发现 — tmux 继承父进程环境

最诡异的事情发生了:

# 父 shell
$ echo $http_proxy
# 空

$ tmux new-session -d -s test
$ tmux send-keys -t test "echo $http_proxy" Enter
$ tmux capture-pane -t test -p
http_proxy=http://x.x.x.x:1081/   # tmux 里居然有值!

tmux show-environment 不显示这个变量。这说明什么?

tmux 默认会继承父进程(创建它的 shell)的环境变量。但父 shell 里明明是空的。真相只有一个——父 shell 的环境变量在某个时刻被设置了,但在我执行 echo 检查之前就已经被清理了。

实际排查后发现:launchd 会将环境变量注入到所有用户进程树中,这些变量在 Terminal.app 启动时就已经存在了,只是在主 shell 里通过 unset 或者特定逻辑清理掉了。但 tmux 作为 launchd 的子进程,在创建新 session 时会重新继承这些变量。

问题分析流程图

第四步:tmux 的环境变量机制

tmux 有两个层面的环境变量管理:

  1. Server 级别 (tmux show-environment -s) — 所有 session 共享
  2. Session/Window 级别 — 每次 new-session 时继承当时父进程的环境

当你执行 tmux new -s session-name 时,tmux 会 fork 一个进程,那个进程继承当时父 shell 的完整环境,包括 launchd 注入的 proxy 变量。


问题根因

根因:launchd 在系统启动时设置的环境变量,被 tmux 在创建新 session 时继承。

  1. Codex/OpenClaw 等桌面应用通过 launchd 设置了 http_proxyhttps_proxy
  2. macOS 的 launchd 将这些变量注入到用户会话的整个进程树
  3. Terminal/iTerm2 启动时加载 .zshrc,可能在某个环节没有正确处理这些变量
  4. 当用户执行 tmux new 时,tmux 进程从父 shell 继承完整的 environ,包括这些 proxy 变量

但更准确地说:问题不在 tmux,而在于 launchd 注入的变量在主 shell 里没有被正确清理

进程树与环境变量继承


解决方案

最简单有效的方案:在 .zshrc 最开头添加一行 unset,在 zsh 启动时主动清理这些不应该存在的 proxy 变量。

# ~/.zshrc 最开头添加

# Clear proxy variables to prevent automatic proxy from external tools
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY

解决方案

为什么这能解决问题?

因为 .zshrc 会在每次启动 zsh shell 时被执行。不管是:

都会先执行 .zshrc,而 unset 会在任何其他配置之前执行,确保 proxy 变量被清理掉。

另一个替代方案: 检查是哪个 launchd 进程设置了这些变量,然后删除对应的 plist 文件。但这样做可能会影响相关应用的正常运行。


验证修复

修改后,重新创建 tmux session:

$ tmux new -s verify
$ echo $http_proxy

# 空!修复成功

总结

项目 内容
问题现象 tmux 新 session 里出现意外的 http_proxy 值
根本原因 launchd 注入的环境变量被 tmux 继承
解决方案 .zshrc 开头 unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
预防措施 定期检查 `env

Q&A

Q: 为什么不直接在 .zshenv 里 unset?

A: .zshenv 在每次执行 zsh 时都会加载(包括子 shell),而 .zshrc 只在 login shell 时加载。但如果 proxy 变量是在 login shell 之后才被设置的(例如 tmux 内部),.zshrc 的 unset 仍然有效。

Q: 会不会影响需要代理的工作?

A: 不会。如果你确实需要代理,可以手动执行 proxy_on alias 来设置。那些 alias 在 .zshrc 里仍然可用。

Q: 怎么查看是谁设置了这个变量?

A: 可以用 launchctl export user | grep proxy 查看 launchd 注入的变量,或者用 ps -e -o pid,ppid,comm | grep <应用名> 来追溯进程树。


如果你也遇到了类似的问题,或者有更好的解决方案,欢迎留言交流!