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

> Codexのself signed certificate in certificate chainは、会社のルートCAを失敗したプロセスに渡して直します。設定は4通りです。

- Source: https://www.aifreeapi.com/ja/posts/codex-self-signed-certificate-in-certificate-chain
- Language: ja
- Published: 2026-10-02
- Updated: 2026-10-02
- Publisher: AI Free API (https://www.aifreeapi.com)

`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になる流れ](https://www.aifreeapi.com/posts/ja/codex-self-signed-certificate-in-certificate-chain/img/proxy-resign-trust-store.webp)

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

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

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

出力の`Certificate chain`にある最後の証明書の`i:`（issuer）を読みます。そこに会社名やセキュリティ製品の名前が出ていれば、通信が途中で再署名されていると判断できます（[openssl s_clientのマニュアル](https://docs.openssl.org/master/man1/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の[認証ドキュメント](https://learn.chatgpt.com/docs/auth)は、社内のTLSプロキシやプライベートなルートCAがあるネットワークでは、ログインの前にこの変数を設定するよう案内しています。

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

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

- **優先順位**：`CODEX_CA_CERTIFICATE`、未設定なら`SSL_CERT_FILE`、どちらもなければシステムのルートです。
- **適用範囲**：ログイン、通常のHTTPSリクエスト、セキュアなWebSocket接続に同じ設定が使われます。この範囲を広げた[プルリクエスト#14239](https://github.com/openai/codex/pull/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](https://github.com/openai/codex/issues/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：ログインの失敗段階から直す](/ja/posts/codex-access-token-could-not-be-refreshed)にあります。

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

デスクトップアプリでは、会社のルートCAがキーチェーンで信頼済みでも失敗する例が報告されています。2026年9月7日の[issue #43489](https://github.com/openai/codex/issues/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の環境変数](https://nodejs.org/api/cli.html)です。検証を弱める設定ではなく、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を繰り返すときの安全な復旧手順](/ja/posts/codex-stuck-reconnecting-fix)の切り分けが使えます。

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

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

[Node.jsのドキュメント](https://nodejs.org/api/cli.html)にある方法は2つです。

| 変数 | 動作 | 向いている場合 |
| --- | --- | --- |
| `NODE_EXTRA_CA_CERTS=/path/to/corp-root.pem` | Node内蔵のルートに、ファイル内のPEM証明書を追加する | PEMファイルが手元にある |
| `NODE_USE_SYSTEM_CA=1` | OSの証明書ストアを読み込む | 会社のルート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](https://github.com/openai/codex/issues/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がコマンドに渡す環境変数を制御します。[設定ドキュメント](https://learn.chatgpt.com/docs/config-file/config-advanced)によると、`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：承認・書き込み・ネットワーク](/ja/posts/codex-config-toml)を参照してください。

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

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

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

![証明書の検証をやめる設定（NODE_TLS_REJECT_UNAUTHORIZED=0など）と、会社のルートCAを信頼させる設定（CODEX_CA_CERTIFICATEなど）の違いを左右に並べた対照図](https://www.aifreeapi.com/posts/ja/codex-self-signed-certificate-in-certificate-chain/img/disable-vs-trust-root-ca.webp)

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

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

| 対象 | 直ったと言える結果 |
| --- | --- |
| PEMファイル | `openssl s_client ... -CAfile`で`Verify return code: 0 (ok)` |
| Codex CLI | `codex 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秒で読み込めないときの切り分け](/ja/posts/codex-timeout)が参考になります。

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

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

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

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