curl で「SSL certificate problem」が出て HTTPS にアクセスできない
SSL certificate problem は、curl がサーバ証明書の発行元を信頼できないと exit code 60 で出る。
CA バンドルを更新するか、社内 CA や自己署名の CA を --cacert で渡せば直る。
公開: 更新:
curl https://localhost:8443/ curl: (60) SSL certificate problem: unable to get local issuer certificate More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the webpage mentioned above.
curl https://localhost:8443/ hello over TLS
2026-10-04 に python:3.12(Python 3.12.15 / Git 2.47.3) で実際に打って取った出力。検証環境の詳細
要約
curl: (60) SSL certificate problem: unable to get local issuer certificate は、サーバが提示した証明書からルート証明書までの経路を curl が作れなかったという意味である。
サーバ側が壊れているとは限らず、curl 側が信頼する CA の一覧に、その経路の起点が入っていないことのほうが多い。
したがって直すべきは「どの CA を信頼させるか」であり、検証を切ることではない。-k を付ければ確かに通るが、その時点で通信相手が本物かどうかを確かめる手段を失う。
実行例
実際に上記の手順を python:3.12 で動かすと、独自 CA で署名した証明書のサーバーに対して素の curl は終了コード 60 で止まり、-v だけでは subject: と issuer: が出ない一方、-vk を併用すると発行元が読める。--cacert や CURL_CA_BUNDLE で CA を渡せば、検証を止めずに本文が取得できる。
$ curl https://localhost:8443/
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.
$ echo $?
60$ curl -v https://localhost:8443/ 2>&1 | grep -E "subject:|issuer:|SSL certificate"
* SSL certificate problem: unable to get local issuer certificate
curl: (60) SSL certificate problem: unable to get local issuer certificate$ curl -vk https://localhost:8443/ 2>&1 | grep -E "subject:|issuer:|SSL certificate"
* subject: CN=localhost
* issuer: CN=Example Corp Root CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.$ curl --cacert corp-ca.pem https://localhost:8443/
hello over TLS
$ echo $?
0$ export CURL_CA_BUNDLE=/tmp/tmp.t8VKtSzwwi/corp-ca.pem$ curl https://localhost:8443/
hello over TLS
$ echo $?
0$ curl -k https://localhost:8443/
hello over TLS
$ echo $?
0— 2026-10-04 時点の出力
検証環境Python 3.12.15 / Git 2.47.3 / 2026-10-04 検証
- 検証日
- 実行環境
python:3.12Debian GNU/Linux 13 (trixie)- バージョン
- Python 3.12.15
- Git 2.47.3
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。
解決策
1. まず証明書の発行元を見る
検証に失敗すると接続がその場で打ち切られるため、curl -v だけでは subject: / issuer: の行が出ず、SSL certificate problem の 1 行しか残らない。
発行元を確かめるときは、調査用に -k を併せて付ける。
curl -vk https://example.com/ 2>&1 | grep -E "subject:|issuer:|SSL certificate"-k はこの確認のためだけに使い、認証情報などは送らない。
curl を使わずに見るなら openssl s_client でも同じ情報が取れる。
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/nullissuer: に社名や見慣れない CA が出れば 2 の可能性が高い。subject: と issuer: が同じなら自己署名である。
2. CA バンドルを更新する
# Debian / Ubuntu
apt-get update && apt-get install -y --reinstall ca-certificates
# Alpine
apk add --no-cache ca-certificatescurl 公式は Mozilla のルート証明書から生成した cacert.pem を配布しているので、パッケージを使えない環境ではこれを取得して使う(Extract CA Certs from Mozilla(新しいタブで開く))。
3. 独自 CA を明示的に渡す
社内 CA や自己署名の CA 証明書が手元にあるなら、それを指定する。
curl --cacert /path/to/corp-ca.pem https://internal.example.com/毎回書きたくない場合は環境変数を使う。
curl は CURL_CA_BUNDLE が設定されていればそのパスを CA バンドルとして読む(Schannel を使う Windows ビルドを除く)。
export CURL_CA_BUNDLE=/path/to/corp-ca.pem4. 中間証明書の欠落はサーバ側で直す
クライアント側に足せるのはルート CA までで、中間証明書はサーバが送るべきものである。openssl s_client -connect example.com:443 -showcerts で送られてくる証明書が 1 枚しかない場合は、サーバの証明書ファイルに中間証明書を連結してもらう。
5. -k は調査用にとどめる
curl -k https://example.com/これは検証そのものを無効にする。
原因の切り分けには使えるが、スクリプトや CI にそのまま残すと中間者攻撃を検知できなくなる。
恒久対応は 2〜4 のいずれかで行う。
よくある原因
- CA バンドルが古い、または存在しない。
最小構成の Docker イメージや古い CI ランナーではca-certificatesが入っていないことがある。 - 社内プロキシが TLS を終端して独自の証明書に差し替えている。
ブラウザは会社が配布した CA を信頼するよう設定済みでも、curl はそれを見ていない。 - 接続先が自己署名証明書を使っている。
開発用のローカルサーバでよくある。 - サーバが中間証明書を送っていない。
ブラウザは不足分を自動で取りに行くことがあるため「ブラウザでは見えるのに curl だけ落ちる」という形で表面化する。