本文へスキップ
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 はデフォルトで、OS のストアではなく、バイナリに組み込まれた Mozilla のルートリストだけを信頼します。そのため、マシンにインストールされた社内のルートは、Node.js にそれを使うよう指示しない限り、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.

    OS の証明書ストアを使う

    --use-system-ca(または環境変数の NODE_USE_SYSTEM_CA=1)を指定すると、Node.js は OS が信頼する証明書を信頼するようになります。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.

    1 つのエージェントに限ってルートを信頼する

    エージェントまたは tls.connect() の ca オプションは、それらの接続のデフォルトのルートを置き換えます。そのため、同じエージェントが公開ホストとも通信する場合は、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());

関連するエラー

署名エラーデータベースをすべて見る