# npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY 的修法

> npm install 报 unable to get local issuer certificate，说明 Node.js 不信任镜像源前面的根证书，多是企业代理或私有仓库。用 cafile 或 NODE_EXTRA_CA_CERTS 解决。

Source: https://ssl.sh/zh/errors/npm-unable-to-get-local-issuer-certificate/
Last updated: 2026-11-10

**`UNABLE_TO_GET_ISSUER_CERT_LOCALLY` 的意思是：npm 镜像源出示的 HTTPS 证书，最终链到了一张 Node.js 不信任的根证书。** 大多数机器上，这是[公司代理](https://ssl.sh/zh/errors/corporate-ssl-inspection/)在重新签名流量，或者私有仓库用了内部 CA。

## 快速修复

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

```bash
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 进程生效：

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

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

```bash
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](https://ssl.sh/zh/errors/node-unable-to-verify-leaf-signature/)，需要修仓库前面的反向代理。

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

- **确认：** `npm config list` 里有 `ca` 或 `cafile`，可能来自项目、用户或全局的 `.npmrc`。
- **修复：** 让这个文件包含所有需要的[根证书](https://ssl.sh/zh/glossary/root-certificate/)（公网的和私有的），或者删掉它改用 `NODE_EXTRA_CA_CERTS`。

### 4. Node.js 或 npm 版本太老

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

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

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

项目或用户目录里的 `.npmrc` 常常还指向已经换了域名或停用的镜像。镜像换了域名、内部镜像换成了内部 CA 签发的证书，都会导致这个报错，或者变成 [CERT_HAS_EXPIRED](https://ssl.sh/zh/errors/node-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：**

```dockerfile
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://`。** 传输过程失去加密和完整性保护。
