装了根证书 Safari 亮绿锁 Chrome 却 ERR_CONNECTION_CLOSED
为内网 HTTPS 站点搭本地开发环境时,我把自签根证书用 security add-trusted-cert 装进了系统 Keychain,满以为搞定了——结果 Safari 绿锁,curl 200,OpenSSL 握手 OK,Chrome 一张白屏,错误码 ERR_CONNECTION_CLOSED,连证书警告页都没有。
这个错误码本身就是陷阱:ERR_CONNECTION_CLOSED 通常意味着 TCP 连接被对端主动关闭,第一反应往往是查服务器、查防火墙、查 nginx——而不是想到浏览器本身。
现象
排查过程的四条关键日志:
openssl s_client ... -CAfile rootCA.crt→Verify return code: 0 (ok),TLS 握手正常curl --cacert rootCA.crt https://内网域名/→ HTTP 403(后端正常响应)- Safari 打开同一地址 → 绿锁,页面加载正常
- nginx
access.log里只有一条 Chrome 的GET / HTTP/1.1 → 301(HTTP 跳 HTTPS 那条),之后没有任何 HTTPS 请求日志——Chrome 在 TLS 握手阶段就悄悄断开了,根本没给服务器机会发证书
最反直觉的一点:不是 NET::ERR_CERT_AUTHORITY_INVALID(那个会弹证书警告页,让你"继续访问"),而是连接层面的 ERR_CONNECTION_CLOSED,Chrome 连"不信任这个证书"的提示都没打印。
根因
Chromium 在进程启动时一次性读取 macOS 系统信任链并缓存到进程内存,之后不再重新读取 Keychain。
security add-trusted-cert 会更新磁盘上的 Keychain 文件和 Keychain 守护进程,但对已运行的 Chrome 进程毫无影响——Chrome 的进程内缓存里依然没有这个新 CA。
当服务器证书的签发链指向 Chrome 进程内缓存不认识的 CA 时,在某些 ALPN/HTTP-2 协商路径下,Chrome 会在 TCP 层面直接关闭连接,而不走"发现不受信任的证书 → 渲染警告页"这条路,因此表现出来的是连接层错误而非证书层错误。
Safari 没这个问题,因为 Safari 底层用的是 NSURLSession,每次连接(或更频繁地)都会重新读取系统信任链。
看到这个报错第一反应是查 nginx 配置、防火墙规则、服务是否崩溃——但如果 curl 和 Safari 同时能通,问题一定在 Chrome 这侧。Chromium 进程级信任链缓存不会因为你操作了 Keychain 而失效,这个行为不会有任何日志警告。
正解
完全退出 Chrome(而不是只关标签页或窗口),再重新打开即可。
1. ⌘+Q 完全退出 Chrome(Quit,不是 ⌘+W 关窗口)
2. 重新打开 Chrome
3. 访问 https://内网域名/
新进程启动时会重新读取 macOS 系统信任链,正确识别刚装入的根证书,页面绿锁正常加载。
Brave / Edge / Arc 等所有 Chromium 内核浏览器同理,均需完整 ⌘+Q 重启。
重启 Chrome 进程是唯一可靠的刷新方式。重启后点击地址栏绿锁可以验证证书链,应当看到你的内网根 CA 出现在链的顶端。
快速验证脚本(绕开浏览器状态)
在确认是否真的是 Chrome 缓存问题、还是服务器侧故障时,用这三条命令做隔离排查:
# 1. TLS 握手验证(不走浏览器)
echo "Q" | openssl s_client -connect 内网域名:443 \
-servername 内网域名 \
-alpn h2,http/1.1 \
-CAfile /path/to/rootCA.crt 2>&1 | grep -E 'Verify return|ALPN'
# 2. curl 带显式 CA bundle
curl --cacert /path/to/rootCA.crt https://内网域名/
# 3. 用 Safari 打开(Safari 每次连接都重读信任链)
open -a Safari https://内网域名/三条全部成功 → 确认是 Chrome 进程内缓存问题 → 重启 Chrome 解决。
Linux 上的 Chrome 使用 NSS DB,读取信任链的频率远高于 macOS,不会出现同样的缓存问题。这个坑是 macOS 特有的。
一句话外卖
macOS 上用
security add-trusted-cert装完根证书,所有已运行的 Chromium 系浏览器都要完整⌘+Q重启,否则进程内信任缓存不刷新,TLS 握手会在连接层静默关闭,而非弹出证书警告页。