本文へスキップ

Codexのself signed certificate in certificate chain:原因と対処法

検証を切らずに直す手順です。再署名した発行者を確認し、会社のルート証明書をPEMで用意して、CLI、アプリ、MCPサーバー、サンドボックス内コマンドのうち失敗したものに渡します。

A
••21 分で読めます•OpenAI Codex
Codexの証明書エラーを表す立体図。接続先chatgpt.comの層の上に証明書チェーンが重なり、最上位のルートCAが未信頼として赤く示されている

self signed certificate in certificate chainは、通信相手が提示した証明書チェーンの最上位が、手元の信頼ストアに入っていない自己署名のルート証明書だった、という意味です。自分で作った証明書の話ではありません。会社支給のPCや社内ネットワークでは、TLSインスペクションを行う社内プロキシやセキュリティ製品がHTTPS通信を会社のルートCAで再署名しており、そのルートCAをCodex側が知らないことがほとんどの原因です。

直し方は一つで、会社のルート証明書をPEM形式で用意し、エラーを出したプロセスが読む設定に渡します。Codexでは通信するプロセスが複数あり、参照する信頼設定がそれぞれ違うため、まず下の表で自分の症状を探してください。

症状が出る場所失敗しているプロセス渡す設定
codex login、CLIの通常の応答、HTTP接続のMCPサーバーCodex本体のクライアントCODEX_CA_CERTIFICATE(未設定ならSSL_CERT_FILE)
デスクトップアプリのRemote Control、ログにSELF_SIGNED_CERT_IN_CHAINアプリのNode層NODE_USE_SYSTEM_CA=1を付けて起動(ユーザー報告)
Node製のMCPサーバーが外部APIを呼ぶとき、npmの実行時そのNodeプロセスNODE_EXTRA_CA_CERTSまたはNODE_USE_SYSTEM_CA=1
Codexが実行したcurlなどがcurl: (60) SSL certificate problemで止まるサンドボックス内のコマンドそのツール自身のCA指定(curlならCURL_CA_BUNDLEや--cacert)

NODE_TLS_REJECT_UNAUTHORIZED=0のように検証そのものを切る設定は、どの行の対処にもなりません。理由は後半で説明します。

self signed certificate in certificate chainの意味

このメッセージはOpenSSLの検証エラー19番です。サーバー証明書から中間証明書をたどった先に自己署名のルート証明書があり、そのルートがクライアントの信頼ストアに登録されていないときに出ます。opensslで確認するとVerify return code: 19 (self signed certificate in certificate chain)と表示されるのも同じ状態です。

よく似たself-signed certificate(エラー18番)は別物で、こちらはサーバー証明書そのものが自己署名の場合です。「in certificate chain」が付いていれば、問題はチェーンの最上位にあります。

会社のネットワークでこのエラーが出る典型的な流れは次のとおりです。

  1. Codexがchatgpt.comなどへHTTPSで接続する。
  2. 社内プロキシやセキュリティエージェントが通信をいったん復号し、会社のルートCAで署名し直した証明書をCodexに返す。
  3. ブラウザはOSの信頼ストアに配布済みの会社のルートCAを見るので通る。OSの信頼ストアを見ないプロセスは、知らないルートとして拒否する。

「ブラウザでは開けるのにCodexだけ失敗する」のはこの3番目の違いによるものです。これは会社のポリシーとして行われている検査であることが多く、対処はその検査を外すことではなく、会社のルートCAをCodex側に信頼させることです。

会社のネットワーク以外では、ウイルス対策ソフトのHTTPSスキャン、プライベートCAで運用している自前のゲートウェイやMCPサーバー、独自のベースURLへの接続でも同じメッセージになります。どの場合も、次の節の方法で「誰が署名したか」を見れば区別できます。

PC上のアプリの通信を社内プロキシが会社のルートCAで再署名し、OSの信頼ストアを見るブラウザは通り、見ないNodeやサンドボックス内のcurlはエラー19になる流れ

opensslで再署名した発行者を確認し、ルート証明書をPEMで用意する

設定を変える前に、チェーンの最後の証明書の発行者を確認します。エラーやログに出ているホスト名があればそれを、なければchatgpt.comを指定します。

bash
openssl s_client -connect chatgpt.com:443 -showcerts </dev/null

出力のCertificate chainにある最後の証明書のi:(issuer)を読みます。そこに会社名やセキュリティ製品の名前が出ていれば、通信が途中で再署名されていると判断できます(openssl s_clientのマニュアル)。外部への接続に明示的なプロキシ指定が必要な環境では、同じマニュアルにある-proxy ホスト:ポートを付けて実行します。

次に、その発行者のルート証明書をPEMファイルとして入手します。

  • 情シスから受け取る:配布用のPEM(.pemや.crt)があるかを聞くのが最も確実です。
  • macOS:キーチェーンアクセスの「システム」キーチェーンで該当のルート証明書を選び、PEM形式で書き出します。ターミナルならsecurity find-certificate -a -c "証明書の名前" -p /Library/Keychains/System.keychain > ~/corp-root.pemでも取り出せます。
  • Windows:certmgr.mscの「信頼されたルート証明機関」から該当の証明書をエクスポートし、形式は「Base-64 encoded X.509 (.CER)」を選びます。これがPEMです。

用意したファイルが正しいルートかどうかは、同じ接続にそのファイルを指定して確かめます。

bash
openssl s_client -connect chatgpt.com:443 -CAfile ~/corp-root.pem </dev/null

最後にVerify return code: 0 (ok)と出れば、このPEMでチェーンを検証できています。19のままなら、別のルート(または中間証明書だけ)を取り出しています。

OSの信頼ストアに該当の証明書が見当たらず、情シスからも入手できない場合は、ここで作業を止めて情シスに相談してください。出どころの分からない証明書を自分で信頼ストアに足すのは、検証を切るのと同じ危険があります。

Codex CLIとログイン:CODEX_CA_CERTIFICATEにPEMのパスを渡す

Codex本体のクライアントは、環境変数CODEX_CA_CERTIFICATEに指定したPEMバンドルを読みます。OpenAIの認証ドキュメントは、社内のTLSプロキシやプライベートなルートCAがあるネットワークでは、ログインの前にこの変数を設定するよう案内しています。

bash
export CODEX_CA_CERTIFICATE="$HOME/corp-root.pem"
codex login

押さえておく点は4つです。

  • 優先順位:CODEX_CA_CERTIFICATE、未設定ならSSL_CERT_FILE、どちらもなければシステムのルートです。
  • 適用範囲:ログイン、通常のHTTPSリクエスト、セキュアなWebSocket接続に同じ設定が使われます。この範囲を広げたプルリクエスト#14239(2026年3月13日マージ)は、対象としてバックエンドのクライアント、クラウドタスク、MCPのHTTPクライアント、音声のHTTP、応答とリアルタイムのWebSocketを挙げています。HTTP接続のMCPサーバーで同じエラーが出る場合も、この変数が対象です。
  • システムのルートは残る:指定したバンドルはシステムのルートに追加して読み込まれるので、PEMには会社のルート証明書だけを入れれば足ります。
  • 読み込みに失敗したとき:エラーメッセージに変数名とパスが出ます。パスの間違いや、PEMではない形式(DER)のファイルを指定していないかを確認します。

毎回効かせるには、~/.zshrcや~/.bashrcにexportの行を追加します。PowerShellなら$env:CODEX_CA_CERTIFICATE = "C:\certs\corp-root.pem"です。

設定しても変わらない場合は、Codexのバージョンを確認します。古いビルドはシステムのルートしか信頼せず、同じ原因でも文言が違いました。2025年11月のissue #6849(CLI 0.58.0)では、カスタムCAのある社内プロキシ配下でログインがToken exchange failed: error sending request for url (https://auth.openai.com/oauth/token )として失敗しています。この文言が出ている場合は、まずCodexを更新してからCODEX_CA_CERTIFICATEを設定してください。証明書以外の原因も含めた段階別の切り分けはCodexのToken Exchange Failed:ログインの失敗段階から直すにあります。

デスクトップアプリのRemote Control:NODE_USE_SYSTEM_CA=1で起動した報告

デスクトップアプリでは、会社のルートCAがキーチェーンで信頼済みでも失敗する例が報告されています。2026年9月7日のissue #43489(アプリ26.901.51231、macOS 26.6.1、Appleシリコン)の内容は次のとおりです。

  • HTTPS検査のあるネットワークで、Remote Controlがself signed certificate in certificate chain(コードSELF_SIGNED_CERT_IN_CHAIN)で接続できない。
  • ログには[remote-control-transport] remote_control_websocket.connect_failed_before_openが残る。
  • 会社のルートCAはシステムキーチェーンで信頼済みで、同じマシンのcurl、OpenSSL、別途インストールしたNodeでは検証が通る。

報告者は、アプリを次のように起動すると接続できたと書いています。

bash
NODE_USE_SYSTEM_CA=1 /Applications/ChatGPT.app/Contents/MacOS/ChatGPT

NODE_USE_SYSTEM_CA=1は、NodeにOSの証明書ストア(macOSのキーチェーン、Windowsの証明書ストア、LinuxではOpenSSLの既定の場所)を読み込ませるNode.jsの環境変数です。検証を弱める設定ではなく、OSがすでに信頼しているルートをNodeにも見せるだけなので、会社のルートCAがOSに配布済みの環境に合います。

ただし、これは1人のユーザーの報告で、2026年10月2日時点でメンテナーの回答は付いていません。アプリのNode層がCODEX_CA_CERTIFICATEを読むかどうか、WindowsやLinuxのアプリで同じ挙動になるかは、公開情報に記載がありません。試す場合は、アプリを完全に終了してから上のコマンドで起動し、Remote Controlがつながるか、ログからconnect_failed_before_openが消えるかで判断します。DockやFinderから起動したアプリには、ターミナルでexportした変数は引き継がれない点にも注意してください。

接続後に切断を繰り返す場合は原因が別にあることもあり、CodexがReconnectingを繰り返すときの安全な復旧手順の切り分けが使えます。

Node製のMCPサーバーとnpm:NODE_EXTRA_CA_CERTSで会社のルートを追加する

stdioで起動するNode製のMCPサーバーが自分で外部APIを呼ぶとき、その通信はCodex本体ではなくNodeのプロセスが行います。CODEX_CA_CERTIFICATEは効かず、Nodeの設定が必要です。npm installやnpxが同じエラーで止まる場合も同じです。

Node.jsのドキュメントにある方法は2つです。

変数動作向いている場合
NODE_EXTRA_CA_CERTS=/path/to/corp-root.pemNode内蔵のルートに、ファイル内のPEM証明書を追加するPEMファイルが手元にある
NODE_USE_SYSTEM_CA=1OSの証明書ストアを読み込む会社のルートCAがOSに配布済み
bash
export NODE_EXTRA_CA_CERTS="$HOME/corp-root.pem"

注意点が2つあります。NODE_EXTRA_CA_CERTSはプロセスの起動時にしか読まれないため、設定後はCodexごと再起動してMCPサーバーを立ち上げ直します。また、ファイルの形式が壊れていても警告が出るだけで起動は続くので、効かないときは起動直後の警告を確認します。NODE_USE_SYSTEM_CA=1は、同梱されているNodeが古いと対応していないことがあります。

変数がMCPサーバーのプロセスまで届いていることも前提です。シェルで設定した変数が起動されたプロセスに渡っているか分からないときは、MCPサーバーの定義側でその変数を明示的に指定します。

サンドボックス内のcurl: (60):コマンドにCAファイルを渡す

Codexが実行したコマンドだけがcurl: (60) SSL certificate problem: self signed certificate in certificate chainで止まり、同じコマンドを普通のターミナルで実行すると通る場合は、サンドボックスが関係しています。

2026年2月のissue #11182(codex-cli 0.98.0、macOS)の報告者の分析では、macOS標準のcurlなどは/etc/ssl/cert.pemにないルートをキーチェーンに問い合わせますが、サンドボックスがその問い合わせ(com.apple.TrustEvaluationAgentへの接続)を拒否します。その結果、キーチェーンには入っているが/etc/ssl/cert.pemにはないルート、つまり会社のルートCAで検証が失敗します。拒否は--log-denialsを付けると確認できます。このissueは未解決で、メンテナーによる回避策は示されていません。

実用的な対処は、キーチェーンに頼らず、ツールにCAファイルを直接渡すことです。これはcurlの標準的な機能で、issueで確認された回避策ではありません。

bash
curl --cacert "$HOME/corp-root.pem" https://example.com
# または
export CURL_CA_BUNDLE="$HOME/corp-root.pem"

環境変数で渡す場合は、Codexが実行するコマンドにその変数が届く必要があります。config.tomlのshell_environment_policyは、Codexがコマンドに渡す環境変数を制御します。設定ドキュメントによると、inherit = "none"は空の環境から始め、inherit = "core"は絞り込んだ一部だけを引き継ぎます。この設定を使っているなら、setで明示します。

toml
[shell_environment_policy]
inherit = "core"
set = { CURL_CA_BUNDLE = "/Users/you/corp-root.pem" }

パスは自分のPEMの絶対パスに置き換えます。Codexに「curl -sI https://chatgpt.comを実行して」と 頼み、curl: (60)が消えてHTTPのステータス行が返れば届いています。サンドボックスの書き込み範囲やネットワーク許可そのものを見直したい場合はCodex サンドボックスと config.toml:承認・書き込み・ネットワークを参照してください。

NODE_TLS_REJECT_UNAUTHORIZED=0が対処にならない理由

NODE_TLS_REJECT_UNAUTHORIZED=0、npm config set strict-ssl false、curl -k(--insecure)は、どれも証明書の検証自体をやめる設定です。エラーは消えますが、会社のプロキシだけでなく、どんな相手が出した証明書でも受け入れる状態になります。Node.jsのドキュメントも、NODE_TLS_REJECT_UNAUTHORIZED=0はTLS接続を中間者攻撃に対して脆弱にすると警告しています。

Codexの通信には、アクセストークン、ソースコード、プロンプトが含まれます。ログイン後のトークンは~/.codex/auth.jsonに保存され、OpenAIのドキュメントはこのファイルをパスワードと同じように扱うよう求めています。その通信で検証を切るのは、「一時的」であっても釣り合いません。ルート証明書を1つ渡せば、検証を保ったまま同じ結果が得られます。

証明書の検証をやめる設定(NODE_TLS_REJECT_UNAUTHORIZED=0など)と、会社のルートCAを信頼させる設定(CODEX_CA_CERTIFICATEなど)の違いを左右に並べた対照図

設定が効いたかの確認と、情シスに相談する場面

確認は、失敗していた操作をもう一度行うのが基本です。

対象直ったと言える結果
PEMファイルopenssl s_client ... -CAfileでVerify return code: 0 (ok)
Codex CLIcodex loginが完了し、新しいセッションで応答が返る
デスクトップアプリRemote Controlが接続し、ログにconnect_failed_before_openが出ない
Node製MCPサーバー、npm再起動後に同じ呼び出しが通る
サンドボックス内のコマンドCodex経由のcurlからcurl: (60)が消える

次のどれかに当たるときは、自分で設定を探し続けずに情シスへ相談します。

  • opensslで見た発行者が会社名でもセキュリティ製品名でもなく、心当たりがない。
  • 会社のルート証明書がOSの信頼ストアになく、PEMも入手できない。
  • 正しいPEMでVerify return code: 0 (ok)になるのに、証明書以外のエラー(接続拒否、認証プロキシの407など)で止まる。
  • 会社の方針として、OpenAIのドメインへの通信が許可されているか分からない。

伝える内容は、失敗した操作、エラーの全文、openssl s_clientで見た発行者名、OSとCodexのバージョンです。~/.codex/auth.jsonの中身やトークンは貼りません。

証明書を渡したあとも起動時にタイムアウトする場合は、プロセスごとにプロキシの設定が違っている可能性があります。Codexがcloud config bundleを15秒で読み込めないときの切り分けが参考になります。

デバイスコードログインなら証明書エラーを避けられますか

避けられません。codex login --device-authはブラウザを開けない環境向けのログイン方法(ベータ。ChatGPTのセキュリティ設定で有効化が必要)ですが、認証ドキュメントのとおりCodexからOpenAIへの通信がHTTPSであることは変わりません。社内プロキシが再署名している限り、同じ場所で同じエラーになります。先にCODEX_CA_CERTIFICATEを設定してからログインしてください。

IDE拡張で同じエラーが出たときはどの設定を見ますか

まずエラーがどの操作で出たかを見ます。ログインやCodexの応答で出るならCodex本体のクライアントなのでCODEX_CA_CERTIFICATE、拡張が起動したNode製のMCPサーバーならNODE_EXTRA_CA_CERTS、Codexが実行したコマンドならそのツールのCA指定です。IDE拡張に固有の証明書設定は、OpenAIのドキュメントに記載がありません。エディタをDockやスタートメニューから起動すると、シェルの設定ファイルに書いた環境変数が引き継がれないことがあるので、変数を設定したターミナルからエディタを起動して結果が変わるかを確かめると切り分けられます。