urllib and "SSL: CERTIFICATE_VERIFY_FAILED" Error の原因と直し方【Dockerで検証済み】
urllib and "SSL: CERTIFICATE_VERIFY_FAILED" Error の原因と解決方法。検証済みの解決コマンド付きで、現象→原因→解決→確認の順に最短で直せます。
発生したエラー
urllib and "SSL: CERTIFICATE_VERIFY_FAILED" Error結論:まずこれで直ります
Python の urllib(内部で ssl モジュールを使用)は HTTPS 接続時にサーバー証明書をシステムの CA バンドルで検証します。下の解決コマンドを順に実行すれば直ります。
pip install --upgrade certifi
python3 -c "
import ssl
import certifi
import urllib.request
# certifi が提供する最新の CA バンドルを明示的に指定して SSLContext を構築する
ctx = ssl.create_default_context(cafile=certifi.where())
urllib.request.urlopen('https://www.python.org/', context=ctx)
print('OK: SSL verification succeeded with certifi CA bundle')
"現象どんなエラーか
次の操作を行うと(検証環境: python:3.11)、上記のエラーが発生します。まずは下の再現コマンドで、同じ状況を再現できることを確認してください。
検証環境:python:3.11
python3 -c "
import ssl
import urllib.request
# 空の SSLContext(CA バンドルを一切ロードしない)を使って HTTPS リクエストを送る
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = True
ctx.verify_mode = ssl.CERT_REQUIRED
# CA をロードしないため certificate verify failed が発生する
urllib.request.urlopen('https://www.python.org/', context=ctx)
"原因なぜ起きるのか
Python の urllib(内部で ssl モジュールを使用)は HTTPS 接続時にサーバー証明書をシステムの CA バンドルで検証します。CA バンドルが存在しない・古い・パスが正しく設定されていない場合、OpenSSL が証明書チェーンを検証できず『SSL: CERTIFICATE_VERIFY_FAILED』を送出します。特に macOS の Python.org 公式インストーラー版や、最小構成の Linux コンテナでよく発生します。根本的な修正は、信頼できる CA 証明書集合を正しく提供することです。最も確実な方法は `pip install --upgrade certifi` で最新の Mozilla CA バンドルを取得し、`ssl.create_default_context(cafile=certifi.where())` のようにそのパスを SSLContext に渡すことです。こうすることで urllib は正しい CA チェーンを参照でき、証明書検証が成功します。`verify=False` や `--trusted-host` のような検証無効化は証明書の真正性チェックを完全に捨てる行為であり、中間者攻撃のリスクを生むため解決策として採用してはなりません。
解決解決手順
pip install --upgrade certifi
python3 -c "
import ssl
import certifi
import urllib.request
# certifi が提供する最新の CA バンドルを明示的に指定して SSLContext を構築する
ctx = ssl.create_default_context(cafile=certifi.where())
urllib.request.urlopen('https://www.python.org/', context=ctx)
print('OK: SSL verification succeeded with certifi CA bundle')
"確認直ったか確認する
python3 -c "
import ssl, certifi, urllib.request
ctx = ssl.create_default_context(cafile=certifi.where())
resp = urllib.request.urlopen('https://www.python.org/', context=ctx)
assert resp.status == 200, f'unexpected status: {resp.status}'
print('verify OK')
"動画で見る
この記事の解決手順は実環境で検証しています
山田 英紀(社内SE 5年以上・13業種以上の業務システムを開発/運用)が、 掲載コマンドを検証環境で実行し、再現〜解決〜確認まで通ることを確認しています。