Claude CodeでCNTLMプロキシ経由が接続拒否になるときの切り分け
WindowsでCNTLMを通すとVS Code拡張は動くのにCLIだけConnection refusedになる報告があります。症状の整理と、PowerShellでの切り分け手順をまとめます。
Claude CodeでCNTLMプロキシ経由が接続拒否になるときの切り分け
WindowsでCNTLMを使う社内プロキシ環境では、Claude Codeの扱いが公式の説明から少しずれます。VS Code拡張は動くのにターミナルのCLIだけ「Connection refused」で止まる報告があり、原因は特定されていません。この記事は、その報告の中身と、手元で原因を絞り込む手順を扱います。
CNTLMはClaude Codeから見るとただのHTTPプロキシ
CNTLMは、NTLM認証を要求する社内プロキシの前に置く、ローカルの中継プログラムです。クライアントは127.0.0.1の待ち受けポートへ普通のHTTPプロキシとして接続し、NTLMの認証は中継側が肩代わりします。
Claude Codeの公式ドキュメントは、NTLMやKerberosのような高度な認証を要するプロキシについて、対応する認証方式を持つLLMゲートウェイの利用を検討するよう書いています。ネットワーク設定のページにCNTLMの記載はありません。
つまりCNTLMを挟む構成は、Claude Codeが直接NTLMを話す形ではなく、「ローカルの素のHTTPプロキシをHTTPS_PROXYで指す」形になります。設定の書き方自体は、通常のプロキシと変わりません。
報告されている症状
GitHubのissue 98367(2026年9月30日起票、open、コメントなし)に、次の構成が報告されています。
報告された環境と症状
環境
Windows 11 Enterprise、Claude Code v2.1.267、PowerShell。CNTLMが
127.0.0.1:3128で待ち受けています。動くもの
VS Code拡張は、
claudeCode.environmentVariablesにプロキシを設定すると接続できます。Invoke-WebRequestに-Proxyを付けてapi.anthropic.comを叩くと、404が返ります。動かないもの
同じ端末・同じ環境変数で
claudeを起動すると、再試行10回のあとに「Connection refused — a firewall or proxy may be blocking it」で止まります。
報告者はHTTP_PROXYとHTTPS_PROXYにhttp://127.0.0.1:3128を入れ、社内CAのバンドルをNODE_EXTRA_CA_CERTSに指定し、Test-NetConnectionでポートの疎通も確かめています。NODE_TLS_REJECT_UNAUTHORIZED=0も試して変わりませんでした。報告者の見立ては「CLIがプロキシ変数を使っていない、あるいは拡張と別の接続経路を使っている」です。
issueには、直近で動いていた版の記載がありません。回帰かどうかの欄は「はい」ですが、原因や修正版は示されていません。以下の手順は、この報告に当てはまるかを手元で確かめるためのもので、原因を断定するものではありません。
「Connection refused」の文言から分かること
Claude Codeの接続エラーは、失敗の種類ごとに文言が分かれています。
| 表示 | 意味 |
|---|---|
| Connection refused — a firewall or proxy may be blocking it | 意味TCP接続そのものが拒否された |
| Couldn't connect through your proxy (ERR_PROXY_TUNNEL) | 意味プロキシはあるが、トンネルを拒否された |
| Can't reach the API server (ENOTFOUND) | 意味名前解決に失敗した |
| Connection dropped (ECONNRESET) | 意味接続が途中で切れた |
報告のメッセージは1行目、つまり接続の拒否です。プロキシに届いたうえで認証や許可で弾かれたなら、2行目のERR_PROXY_TUNNELが出る作りになっています。ここから考えられる分岐は2つです。
- プロキシへ向かっていない: プロキシ変数が
claudeのプロセスに届かず、api.anthropic.comへ直接つなぎに行って拒否されている - プロキシのポートで拒否されている: 向き先は正しいが、そのアドレスとポートで待ち受けがない
どちらかを分けるのが、次の手順の目的です。NODE_TLS_REJECT_UNAUTHORIZEDやNODE_EXTRA_CA_CERTSはTLSの段階の設定なので、TCP接続が拒否される段階の問題には効きません。
PowerShellで切り分ける手順
1. プロキシが生きているかを、curlで直接確かめる
PowerShellではcurlがInvoke-WebRequestの別名になるため、curl.exeと拡張子付きで呼びます。-xでプロキシを指定すると、CNTLM経由の経路が通るかを、Claude Codeを使わずに確認できます。
curl.exe -x http://127.0.0.1:3128 -I https://api.anthropic.com
Test-NetConnection 127.0.0.1 -Port 3128ここで失敗するなら、Claude Code以前の問題です。CNTLMの起動状態と待ち受けアドレスを見直します。報告者のように成功するなら、プロキシ自体は無実です。
2. 失敗する端末で、変数が見えているかを確かめる
環境変数は、設定したあとに開き直した端末にしか届きません。claudeを起動するのと同じウィンドウで確認します。
$env:HTTPS_PROXY
$env:HTTP_PROXY
$env:NO_PROXY
$env:ANTHROPIC_BASE_URLANTHROPIC_BASE_URLに値があると、リクエストの宛先がそちらに変わります。死んだローカルアドレスを指す値が残っていると、プロキシ設定とは無関係に接続拒否になります。この罠はConnection refusedの原因と切り分けで詳しく扱っています。
3. Claude Codeが読んだプロキシを、/statusで見る
claudeを起動し、/statusのProxy行を見ます。公式ドキュメントによると、この行は有効なプロキシのURLを示し、解釈できない値は「無効で無視された」と表示されます。ここが空なら、変数はclaudeのプロセスに届いていません。http://127.0.0.1:3128が出ていれば、向き先は正しく読まれています。
4. デバッグログで接続の失敗を見る
claude --debug-file $env:TEMP\claude-debug.txt--debug-fileを付けると、ログの出力先を指定できます。付けない場合は~/.claude/debug/<session-id>.txtに書かれます。公式ドキュメントが挙げる確認行は、証明書の読み込みを示すCA certs: Appended extra certificates from NODE_EXTRA_CA_CERTSのような行です。社内CAのバンドルが読めているかは、ここで分かります。
変数の置き場所を、シェルから設定ファイルへ移す
VS Code拡張が動いた設定は、環境変数をシェルではなく拡張の設定に書く形でした。CLI側でも同じ発想で、変数を~/.claude/settings.jsonのenvブロックに置けます。公式ドキュメントは、ネットワーク関連の変数をこのブロックに置けると説明しています。シェルの継承に頼らないので、端末ごとの差が出にくくなります。
{
"env": {
"HTTPS_PROXY": "http://127.0.0.1:3128",
"HTTP_PROXY": "http://127.0.0.1:3128",
"NO_PROXY": "localhost,127.0.0.1"
}
}書き換えたあとはclaudeを起動し直し、/statusのProxy行で反映を確かめます。バックグラウンドエージェントを使う場合は、シェルのexportだけでは届かないことがあり、公式ドキュメントも設定ファイルへの記載を勧めています。プロキシ周りの設定全体はClaude Codeのプロキシ設定にまとめています。
変数が複数あるときの優先順位に注意する
プロキシ変数は大文字だけでなく小文字も読まれます。公式ドキュメントによると、Claude Codeはhttps_proxy、HTTPS_PROXY、http_proxy、HTTP_PROXYの順で、最初に設定されているものを使います。
社内の配布設定や過去の試行で、小文字のhttps_proxyが別のアドレスで残っていると、新しく設定したHTTPS_PROXYより先に採用されます。報告者は大文字の2変数を確認していますが、小文字の変数についての記載はありません。手順2の確認に、小文字も加えておくと漏れがありません。
Get-ChildItem Env: | Where-Object Name -like "*proxy*"この1行で、大文字小文字を問わずプロキシ関連の変数が一覧できます。値が2種類見つかったら、使われる側の順位を上の表記順で確かめます。
また、プロキシのURLにhttp://のようなスキームを付けず127.0.0.1:3128とだけ書くと、起動時に変数名つきのエラーで止まります。今回の症状は起動後の接続拒否なので、こちらとは別の問題です。
バージョンを上げて、同じ症状かを見直す
issueの環境はv2.1.267です。changelogには、その後の版で次のような修正が入っています。
- v2.1.292:
HTTPS_PROXYを設定していると、Claude Code自身のAPIリクエスト(サインインなど)でNO_PROXYが無視される不具合を修正
この修正がissueの症状と同じ原因かは、記載がありません。ただし、プロキシ周りの修正は版を追って入っています。claude --versionで版を確かめ、古ければ更新してから、上の手順をもう一度試す価値があります。
それでも直らないとき
手順1が成功し、手順3でProxy行も正しいのにCLIだけが失敗する場合は、issue 98367と同じ状況です。次の2点を持ち寄ると、報告が前に進みやすくなります。
claude --versionと、/statusのProxy行の表示- デバッグログの接続失敗の行(ホスト名やユーザー名は伏せる)
当面の回避策としては、公式ドキュメントの案内どおり、NTLMなどの認証をゲートウェイ側で受けるLLMゲートウェイを立てる方法があります。社内に既存のゲートウェイがあるなら、ANTHROPIC_BASE_URLでそこを指す形です。
よくある質問
CNTLMの設定ファイルはどう書けばよいですか
CNTLM自体の設定は、Claude Codeの公式ドキュメントの範囲外です。Claude Code側に必要なのは、CNTLMが待ち受けているアドレスとポートをHTTPS_PROXYに書くことだけです。
VS Code拡張で動くのに、CLIで動かないのはなぜですか
報告では、拡張はclaudeCode.environmentVariablesに設定した変数で動いています。CLI側は端末の環境変数を使うので、変数がclaudeのプロセスに届いているかを/statusで確かめるのが最初の一手です。
まとめ
CNTLMを挟む構成では、Claude Codeは通常のHTTPプロキシと同じ扱いで、ドキュメントにCNTLM固有の手順はありません。「Connection refused」が出たら、curl.exe -xでプロキシの生死を、/statusのProxy行でClaude Codeが何を読んだかを確かめると、「プロキシに向かっていない」と「向かった先で拒否された」を分けられます。issue 98367の原因は未特定なので、直らない場合は版とログを添えて報告するのが確実です。