Skip to content

企業環境 Node.js SSL 憑證錯誤修復

在 Zscaler 等 TLS inspection 環境下,透過 NODE_EXTRA_CA_CERTS 讓 Node.js 信任企業自簽 CA,修復 UNABLE_TO_GET_ISSUER_CERT_LOCALLY 錯誤。

概述

企業網路通常會在 HTTPS 連線上注入自訂 CA(Zscaler、Palo Alto、Fortinet 等),對所有 TLS 流量進行解密再重新加密(TLS inspection)。瀏覽器和作業系統通常已預先信任這些企業 CA,但 Node.js 使用自己內建的 CA bundle,不讀取系統憑證庫,因此會出現以下錯誤:

SSL certificate error (UNABLE_TO_GET_ISSUER_CERT_LOCALLY)

解決方式是透過環境變數 NODE_EXTRA_CA_CERTS 指向企業根 CA 憑證,讓 Node.js 的 TLS 驗證補入這份額外信任。此方式適用於 npm、npx、Claude Code、以及所有使用 Node.js https module 的工具。

核心內容

識別 TLS Inspection 環境

開啟任意 HTTPS 網站,查看憑證鏈頂端的根 CA:若根憑證是公司名稱或防火牆廠商(Zscaler、Palo Alto、Fortinet、Cisco Umbrella 等)而非公開 CA(DigiCert、Let's Encrypt、GlobalSign),即為 TLS inspection 環境。

NODE_EXTRA_CA_CERTS 機制

Node.js 提供 NODE_EXTRA_CA_CERTS 環境變數作為追加信任的標準做法。設定後,Node.js 在建立 TLS 連線時會同時信任內建 CA bundle 與此環境變數指向的憑證檔。憑證內容必須為 PEM 格式(-----BEGIN CERTIFICATE----- 開頭),副檔名不限(.pem.crt.cer 皆可)。

WSL 系統層級信任

若需要讓 curl、Python requests、系統套件管理等非 Node.js 工具也信任企業 CA,需將憑證安裝至 WSL Ubuntu 的系統 CA 庫。注意:update-ca-certificates 只讀取 /usr/local/share/ca-certificates/ 下的 .crt 副檔名,副檔名錯誤會靜默略過。

常見踩坑:檔名含空白

NODE_EXTRA_CA_CERTS 載入失敗最常見原因是檔名含空白(例如 Zscaler Root CA.crt),Node.js 會輸出 ignoring extra certs from ..., load failed 並靜默略過。務必確保憑證檔名無空白。

關鍵要點

  • Node.js 不讀取 Windows 或 Linux 系統憑證庫,需手動設定 NODE_EXTRA_CA_CERTS
  • 憑證檔名不能含空白,否則 Node.js 靜默略過不報錯
  • NODE_EXTRA_CA_CERTS 副檔名不限,內容必須是 PEM 格式
  • WSL 中 update-ca-certificates 要求副檔名必須是 .crt
  • Root CA 通常有效 10–20 年,不需頻繁更新

實際應用

此問題最常出現在:

  • Claude Code / npm 在企業環境首次安裝或更新時
  • CI/CD pipeline 在 corporate proxy 後執行 npm install
  • WSL 開發環境存取公司內網 HTTPS API

部署設定參考

以下為實際設定步驟,供查詢與複製使用。

步驟一:取得企業 CA 憑證(Windows)

  1. Chrome 開啟任意 HTTPS 網站(如 https://google.com
  2. 點網址列鎖頭 →「連線是安全的」→「憑證有效」
  3. 切到「憑證路徑」分頁,找到最頂層的根憑證(如 Zscaler Root CA
  4. 點選 →「檢視憑證」→「詳細資料」→「複製到檔案」
  5. 匯出格式選 Base-64 編碼 X.509 (.CER)

環境參數

項目
憑證存放路徑(Windows)D:\cert\zscaler-root-ca.crt
憑證存放路徑(WSL)~/.local/share/certs/zscaler-root-ca.crt
環境變數名稱NODE_EXTRA_CA_CERTS
憑證格式PEM(-----BEGIN CERTIFICATE-----
update-ca-certificates 存放路徑/usr/local/share/ca-certificates/

操作指令

Windows PowerShell 設定:

powershell
# 臨時生效(本 Session)
$env:NODE_EXTRA_CA_CERTS = "D:\cert\zscaler-root-ca.crt"

# 永久設定(User 層級環境變數)
[Environment]::SetEnvironmentVariable("NODE_EXTRA_CA_CERTS", "D:\cert\zscaler-root-ca.crt", "User")

WSL 設定:

bash
# 建立憑證目錄並複製憑證(從 Windows 複製過來)
mkdir -p ~/.local/share/certs
cp /mnt/c/certs/zscaler-root-ca.crt ~/.local/share/certs/zscaler-root-ca.crt

# 在 ~/.zshrc 或 ~/.bashrc 加入
export NODE_EXTRA_CA_CERTS="$HOME/.local/share/certs/zscaler-root-ca.crt"

# 生效
source ~/.zshrc

(可選)WSL 系統層級信任(curl、Python 等工具同時受益):

bash
sudo cp ~/.local/share/certs/zscaler-root-ca.crt /usr/local/share/ca-certificates/zscaler-root-ca.crt
sudo update-ca-certificates

修正檔名含空白問題:

bash
mv ~/.local/share/certs/"Zscaler Root CA.crt" ~/.local/share/certs/zscaler-root-ca.crt
export NODE_EXTRA_CA_CERTS="$HOME/.local/share/certs/zscaler-root-ca.crt"

驗證:

bash
# 測試 Node.js TLS
node -e "require('https').get('https://registry.npmjs.org', r => { console.log('OK', r.statusCode); r.resume(); })"

# 測試系統層級
curl -v https://registry.npmjs.org

# 查看憑證有效期
openssl x509 -in ~/.local/share/certs/zscaler-root-ca.crt -noout -dates

副檔名對照

工具副檔名要求內容格式
NODE_EXTRA_CA_CERTS不限(.pem.crt.cer 皆可)PEM
update-ca-certificates必須 .crtPEM

相關概念

來源