跳到主要内容
ecdsa.com
Node.js tls / https / fetchNode.js crypto 模块

错误信息

unable to get local issuer certificate

也可能输出为

  • code: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY'
  • UNABLE_TO_GET_ISSUER_CERT_LOCALLY

使用 fetch() 时,这条消息作为“TypeError: fetch failed”的原因出现;npm 把同样的情况报告为“npm error code UNABLE_TO_GET_ISSUER_CERT_LOCALLY”。如果服务器只发送叶子证书,Node.js 报告的则是“unable to verify the first certificate”。

Node.js tls / https / fetch — code UNABLE_TO_GET_ISSUER_CERT_LOCALLY (OpenSSL X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 20)

含义

服务器发送了证书链(叶子证书和中间证书),但链顶端的证书由 Node.js 不信任的根证书签发。默认情况下,Node.js 只信任编译进二进制文件的 Mozilla 根证书列表,而不信任操作系统的证书存储,因此安装在机器上的公司根证书对它来说是不可见的,除非你让 Node.js 使用它。

原因

修复方法

  1. 1.

    NODE_EXTRA_CA_CERTS

    让 Node.js 指向一个 PEM 文件,其中的证书会添加到内置根证书中。该变量只在进程启动时读取一次,因此在正在运行的应用内部导出它不会产生任何效果。

    bash
    # Adds to Node's built-in roots; read once at start-up, so set it before node starts
    export NODE_EXTRA_CA_CERTS=/etc/ssl/private-ca/corp-root-ca.pem
    node app.js
  2. 2.

    使用操作系统的证书存储

    --use-system-ca(或环境中的 NODE_USE_SYSTEM_CA=1)让 Node.js 信任操作系统所信任的证书,而这正是 IT 部门管理的内容。

    bash
    # Trust what the operating system trusts (Windows store, macOS Keychain, Linux bundle)
    node --use-system-ca app.js
    # or, for tools that start node themselves (npm scripts, CLIs):
    NODE_USE_SYSTEM_CA=1 npx some-cli
  3. 3.

    在代码中设置默认 CA 列表

    tls.setDefaultCACertificates() 会替换默认列表,此后由 https、tls 和 fetch 建立的连接都会使用新列表;如示例所示,把现有的默认证书与你的根证书合并。

    js
    // In code, before the first request: keep the default roots and add yours.
    // Applies to https, tls and fetch. Available in current Node.js 22 and 24 releases.
    import tls from "node:tls";
    import fs from "node:fs";
    
    tls.setDefaultCACertificates([
      ...tls.getCACertificates("default"),
      fs.readFileSync("/etc/ssl/private-ca/corp-root-ca.pem", "utf8"),
    ]);
  4. 4.

    只为一个 Agent 信任该根证书

    Agent 或 tls.connect() 的 ca 选项会替换这些连接的默认根证书;如果同一个 Agent 还要访问公共主机,就要把 tls.rootCertificates 一并加入。

    js
    // Per-agent trust: note that "ca" REPLACES the default roots, so append them
    import https from "node:https";
    import tls from "node:tls";
    import fs from "node:fs";
    
    const agent = new https.Agent({ ca: [...tls.rootCertificates, fs.readFileSync("corp-root-ca.pem", "utf8")] });
    https.get("https://api.example.com/", { agent }, (res) => res.resume());

相关错误

浏览完整的签名错误库