命令行工具

npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY 的修法

作者: · 最后核实

报错信息npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY

UNABLE_TO_GET_ISSUER_CERT_LOCALLY 的意思是:npm 镜像源出示的 HTTPS 证书,最终链到了一张 Node.js 不信任的根证书。 大多数机器上,这是公司代理在重新签名流量,或者私有仓库用了内部 CA。

快速修复

把证书链顶端的根证书交给 Node.js 就能解决,通常用 NODE_EXTRA_CA_CERTS。先看 npm 实际看到的证书是谁签发的:

npm config get registry
openssl s_client -connect registry.npmjs.org:443 -servername registry.npmjs.org </dev/null 2>/dev/null | openssl x509 -noout -issuer

如果用的是国内镜像,把上面的主机名换成 registry.npmmirror.com。签发者如果是公司名或安全厂商,向 IT 要这张根证书(PEM 格式),对所有 Node.js 进程生效:

export NODE_EXTRA_CA_CERTS=/path/to/company-root.pem
npm install

写进 shell 配置、CI 变量或 Dockerfile 里长期生效。也可以只配置 npm:

npm config set cafile /path/to/ca-bundle.pem   # 文件里要包含 npm 需要的全部根证书

新版 npm 的输出是 npm error code UNABLE_TO_GET_ISSUER_CERT_LOCALLY,修法一样。

原因

最常见的是公司代理解密 HTTPS,其次是私有仓库用了内部 CA。

1. 公司代理、上网行为管理或杀毒软件解密 HTTPS

  • 确认: 上面打印的签发者不是公网 CA;只在公司网络或连 VPN 时报错。
  • 修复: 用 NODE_EXTRA_CA_CERTS 加入代理根证书。如果还需要配置代理地址,用 npm config set proxy 和 https-proxy 设置成 IT 提供的地址。

2. 私有仓库用了内部 CA

公司自建的 Nexus、Artifactory、Verdaccio、GitLab 包仓库经常用内部 CA 签发的证书。

  • 确认: npm config get registry 是内网地址,签发者是公司内部 CA。
  • 修复: 用 NODE_EXTRA_CA_CERTS 信任内部根证书,或者在项目 .npmrc 里配置 cafile。如果私有仓库证书链不完整,报错会变成 unable to verify the first certificate,需要修仓库前面的反向代理。

3. ca 或 cafile 配置把默认列表替换掉了

  • 确认: npm config list 里有 ca 或 cafile,可能来自项目、用户或全局的 .npmrc。
  • 修复: 让这个文件包含所有需要的根证书(公网的和私有的),或者删掉它改用 NODE_EXTRA_CA_CERTS。

4. Node.js 或 npm 版本太老

很老的版本内置的 CA 列表也很旧。

  • 修复: 升级到仍在维护的 Node.js LTS 版本。国内下载 Node.js 安装包可以用 npmmirror 提供的二进制镜像,或者用 nvm、fnm 等版本管理工具配置国内镜像地址。

5. 镜像地址过时或已停用

项目或用户目录里的 .npmrc 常常还指向已经换了域名或停用的镜像。镜像换了域名、内部镜像换成了内部 CA 签发的证书,都会导致这个报错,或者变成 CERT_HAS_EXPIRED 之类的错误。典型例子是还在用旧淘宝镜像域名的老项目。

  • 确认: npm config get registry 和 npm config list 显示的地址不是你预期的;对它执行 npm ping 失败。
  • 修复: 用 npm config set registry https://registry.npmmirror.com 改成当前地址,或者删掉 .npmrc 里那一行过时的配置。团队项目要把修改提交到仓库,其他同事才不会继续报错。

各环境修法

Windows、macOS、Docker 和 CI 上都用 NODE_EXTRA_CA_CERTS 指向 PEM 根证书,并且要在 npm 启动之前设置好。

Windows: 先从 Windows 证书库把根证书导出为“Base64 编码 X.509”格式,再执行 setx NODE_EXTRA_CA_CERTS C:\certs\company-root.pem,然后重新打开终端。

macOS / Linux: 在 ~/.zshrc 或 ~/.bashrc 中加入 export NODE_EXTRA_CA_CERTS=...。

Docker:

COPY company-root.pem /usr/local/share/ca-certificates/company-root.crt
ENV NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/company-root.crt
RUN npm config set registry https://registry.npmmirror.com && npm ci

CI(GitLab CI、Jenkins、云效、GitHub Actions): 把 PEM 存成文件类型的密钥,npm ci 之前导出 NODE_EXTRA_CA_CERTS。

安装时会下载二进制文件的包(如浏览器内核、原生模块)用的是它们自己的 HTTPS 请求,只有 NODE_EXTRA_CA_CERTS 能覆盖,npm 的 cafile 管不到。

不要这样做

关闭 strict-ssl、关闭 TLS 校验或改用 http://,都会让包在下载途中可以被篡改。

  • npm config set strict-ssl false。 npm 会接受镜像源的任何证书,中间人可以返回被篡改的包,再通过安装脚本在你机器上执行代码。
  • NODE_TLS_REJECT_UNAUTHORIZED=0 npm install。 风险一样,而且作用于安装过程中的所有连接。
  • 把镜像源改成 http://。 传输过程失去加密和完整性保护。

常见问题

cafile 和 NODE_EXTRA_CA_CERTS 用哪个?

公司代理环境下一般选 NODE_EXTRA_CA_CERTS。cafile 只由 npm 读取,和 ca 一样会替换 npm 默认的 CA 列表,文件里必须包含所有需要的根证书。NODE_EXTRA_CA_CERTS 是在 Node.js 默认列表基础上追加,对所有 Node.js 进程有效,安装时下载二进制文件的 postinstall 脚本也能用上。

为什么只在公司网络里报错?

公司的代理或上网行为管理在解密 HTTPS,并用自己的根证书重新签名。在家直连镜像源时看到的是公网证书。向 IT 要这张代理根证书即可。

用 npmmirror 国内镜像也会报这个错吗?

镜像本身证书正常时不会。但如果 .npmrc 里还写着已停用的旧淘宝镜像域名 registry.npm.taobao.org,就会出现证书报错(通常是证书过期),请改为 https://registry.npmmirror.com。见 CERT_HAS_EXPIRED。

yarn、pnpm 要单独配置吗?

它们都运行在 Node.js 上,设置 NODE_EXTRA_CA_CERTS 后都能生效。它们也各有自己的 CA 配置项(如 yarn 的 cafile、Yarn Berry 的 caFilePath),想按工具单独配置时才需要。

资料来源

  1. npm config (cafile, ca, strict-ssl, registry)
  2. Node.js command-line options (NODE_EXTRA_CA_CERTS)
  3. Node.js TLS (SSL) documentation