PHP Warning: file_get_contents() SSL operation failed — certificate verify failed の修正方法

intermediate🐘 PHP2026-05-17| PHP 5.6+、PHP 7.x、PHP 8.x(Linux: Ubuntu、Debian、CentOS)、macOS、Windows(XAMPP/WAMP);Apache または Nginx + OpenSSL

Error Message

Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
#php#file_get_contents#ssl#openssl#https#curl

TL;DR — 素早い修正

PHPがfile_get_contents()でHTTPS URLを呼び出す際、リモートサーバーのSSL証明書を検証できない状態です。php.iniで有効なCAバンドルをPHPに指定すれば解決します:

; php.ini に記述
openssl.cafile=/etc/ssl/certs/ca-certificates.crt

保存後にWebサーバーを再起動してください。ローカルスクリプトで今すぐ動かす必要がある場合は、ストリームコンテキストのアプローチに進んでください。ただし、本番環境でSSL検証を無効にするのは避けてください。本当に危険です。

エラーの内容

Warning: file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed

Warning: file_get_contents(): Failed to enable crypto
Warning: file_get_contents(https://example.com/api): failed to open stream: operation failed

PHPがfile_get_contents()でHTTPS URLを取得しようとした際、OpenSSLがリモート証明書を信頼された認証局(CA)と照合できない場合にこのエラーが発生します。主に以下の状況で起きます:

  • php.iniにCAバンドルが設定されていない、PHPを新規インストールした直後
  • PHPをアップグレードしたが、openssl.cafileが古いまたは削除されたバンドルを指している
  • 対象サーバーが自己署名証明書または期限切れの証明書を使用している
  • macOS、XAMPP、またはWAMP — これらはOpenSSLにシステムCAルートが組み込まれていない状態で配布される

修正1 — php.ini でCAバンドルを指定する(推奨)

まず、CAバンドルがシステムに実際に存在するか確認します:

# Debian / Ubuntu
ls /etc/ssl/certs/ca-certificates.crt

# CentOS / RHEL / Fedora
ls /etc/pki/tls/certs/ca-bundle.crt

# macOS (`brew install openssl` 実行後)
ls /usr/local/etc/openssl/cert.pem

php --iniで使用中のphp.iniを確認し、[openssl]セクションに以下の2行を追加します:

[openssl]
openssl.cafile=/etc/ssl/certs/ca-certificates.crt
curl.cainfo=/etc/ssl/certs/ca-certificates.crt

両方の行が必要です。1行目はfile_get_contents()に、2行目はcurl拡張モジュールに対応します。サーバーを再起動してください:

# Apache
sudo systemctl restart apache2   # または httpd

# Nginx + PHP-FPM
sudo systemctl restart php8.2-fpm
sudo systemctl restart nginx

修正2 — file_get_contents の代わりに curl を使う

正直なところ、この用途にはcurlの方が適しています。タイムアウト、リダイレクト、SSLを明示的に制御でき、問題が発生した際のエラーメッセージも読みやすいです:

<?php
function https_get(string $url): string|false {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_CAINFO         => '/etc/ssl/certs/ca-certificates.crt',
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_TIMEOUT        => 15,
    ]);
    $response = curl_exec($ch);
    if (curl_errno($ch)) {
        error_log('curl error: ' . curl_error($ch));
    }
    curl_close($ch);
    return $response;
}

$data = https_get('https://example.com/api/data');

CURLOPT_SSL_VERIFYHOST => 2に注目してください — これが正しい値です(trueでも1でもありません)。値2は、証明書のCommon Nameが接続先のホスト名と実際に一致するかをcurlが確認するよう指定します。

修正3 — 新しいCAバンドルをダウンロードする(XAMPP / WAMP / macOS)

WindowsとmacOSの開発環境はシステムCAルートなしで配布されることが多いです。curlプロジェクトが管理するMozilla CAバンドルを一度ダウンロードするだけで解決します:

# cacert.pem をダウンロード
curl -o /path/to/cacert.pem https://curl.se/ca/cacert.pem

次にphp.iniで設定します:

openssl.cafile=C:\xampp\php\extras\ssl\cacert.pem
curl.cainfo=C:\xampp\php\extras\ssl\cacert.pem

XAMPPコントロールパネルからApacheを再起動してください。バンドルは約200 KBで、主要なパブリックCAをすべてカバーしています。

修正4 — ストリームコンテキストを使う(リクエストごとのCAパス指定)

php.iniにアクセスできない場合は、呼び出し時に直接CAバンドルを指定します:

<?php
$context = stream_context_create([
    'ssl' => [
        'verify_peer'       => true,
        'verify_peer_name'  => true,
        'cafile'            => '/etc/ssl/certs/ca-certificates.crt',
    ],
]);

$result = file_get_contents('https://example.com/api', false, $context);

これはphp.iniをグローバルに編集できない共有ホスティング環境で便利です。検証は有効のまま — 正しいバンドルを指定するだけです。

最終手段 — SSL検証を無効にする(開発環境限定)

ローカルマシンのみで使用してください。これにより証明書の保護がすべて取り除かれ、中間者攻撃の危険にさらされます。本番環境には絶対にデプロイしないでください:

<?php
// ⚠️ 開発環境限定 — MITM攻撃のリスクあり
$context = stream_context_create([
    'ssl' => [
        'verify_peer'      => false,
        'verify_peer_name' => false,
    ],
]);

$result = file_get_contents('https://example.com/api', false, $context);

特殊ケース — 自己署名証明書

内部サービスでは自己署名証明書がよく使われます。検証を無効にするのではなく、システムCAバンドルの代わりにその特定の証明書をPHPに指定してください:

<?php
$context = stream_context_create([
    'ssl' => [
        'verify_peer'       => true,
        'verify_peer_name'  => true,
        'cafile'            => '/path/to/self-signed-cert.pem',
    ],
]);

$result = file_get_contents('https://internal-service.local/api', false, $context);

ブラウザから証明書をエクスポートするか、内部サービスの管理者に.pemファイルを依頼してください。

修正の確認

ターミナルから簡単なサニティチェックを行います:

# CLIでテスト
php -r "echo file_get_contents('https://www.google.com') ? 'OK' : 'FAILED';"

それでも失敗する場合は、PHPが実際に読み込んでいるCAファイルを確認します:

php -r "print_r(openssl_get_cert_locations());"

default_cert_fileを確認してください。空白または存在しないパスを指している場合、それが原因です。php.iniopenssl.cafileを実際に存在するパスに設定してください。

サーバーを再起動した後、PHPが更新された設定を読み込んでいるか確認します:

php -i | grep -E 'cafile|cainfo'

OS上のCA証明書を更新する

バンドル自体が古い場合もあります — 特にしばらく手が付けられていないサーバーでよく起こります。OSのパッケージマネージャーから再インストールしてください:

# Debian / Ubuntu
sudo apt-get install --reinstall ca-certificates
sudo update-ca-certificates
# CentOS / RHEL
sudo yum reinstall ca-certificates
sudo update-ca-trust

Related Error Notes