シナリオ
AWS CloudFrontディストリビューションをオリジンの前面に配置するように設定したとします。オリジンはEC2上のNginxサーバー、Application Load Balancer (ALB)、あるいはカスタムのNodeJSアプリかもしれません。HTTP経由ではすべて完璧に動作します。しかし、Origin Protocol PolicyをHTTPSに切り替えた瞬間、ユーザーは次のような不快なエラーに直面します:
502 Bad Gateway: CloudFront was unable to establish a secure connection with the origin server.
これは、CloudFrontのエッジロケーションとバックエンド間のセキュアハンドシェイクが失敗したときに発生します。ブラウザでユーザーが表示している証明書とは関係ありません。代わりに、AWSとサーバー間の信頼関係が具体的に損なわれていることが原因です。
なぜCloudFrontは接続を拒否するのか
CloudFrontはSSL証明書に対して非常に厳格であることで知られています。ブラウザでは「そのまま続行」をクリックして警告を無視できるかもしれませんが、CloudFrontはセキュリティを維持するために単純に接続を切断します。主な原因は以下の通りです:
- 自己署名証明書: CloudFrontは信頼された認証局(CA)からの証明書を必要とします。カスタムオリジンに対して自己署名証明書を信頼することはありません。
- 有効期限切れの証明書: 証明書の期限が5分前に切れただけでも、ハンドシェイクは即座に失敗します。
- 証明書チェーンの不備: サーバーがドメイン証明書を送信していても、中間証明書を忘れている場合があります。これによりチェーンが不完全になります。
- プロトコルの不一致: サーバーがTLS 1.0のような古いプロトコルしかサポートしていない一方で、CloudFrontはTLS 1.2または1.3を要求している可能性があります。
- SNIとホスト名の不一致: CloudFrontの
Origin Domain Nameがorigin.example.comであるのに、証明書がwww.example.comしかカバーしていない場合、ハンドシェイクは失敗します。
クイック修正:「緊急用」パス
サイトがダウンしており、デバッグ中にサービスを即座に復旧させる必要がある場合は、一時的にSSL要件を回避できます。これはあくまで一時しのぎとして行ってください。
- CloudFront Consoleを開きます。
- ディストリビューションに移動し、Originsタブをクリックします。
- オリジンを選択し、Editをクリックします。
- Origin Protocol Policyを**"HTTP Only"**に切り替えます。
- 保存し、ディストリビューションがデプロイされるまで待ちます(通常2〜5分)。
これにより、CloudFrontはポート80経由でサーバーと通信し、SSLハンドシェイクを完全にスキップします。
恒久的な修正:SSLハンドシェイクの解決
1. OpenSSLでオリジン証明書を検査する
推測はやめて、サーバーが外部に提示している内容を正確に確認しましょう。ターミナルから次のコマンドを実行します(origin.example.comをバックエンドの実際のアドレスに置き換えてください):
openssl s_client -connect origin.example.com:443 -servername origin.example.com -showcerts
出力結果で、特に次の2点を確認してください:
- Verify return code:
0 (ok)と表示されている必要があります。それ以外のコードが表示される場合、CloudFrontは接続を拒否します。 - Certificate chain: 出力に少なくとも2つの証明書(サーバー証明書と中間CA証明書)が表示されている必要があります。
2. ホスト名を一致させる
よくある間違いは、デフォルトのAWS割り当て名を使用することです。CloudFrontのオリジンがmy-load-balancer-12345.us-east-1.elb.amazonaws.comを指しているのに、SSL証明書がapi.myapp.comに対して発行されている場合、ハンドシェイクは失敗します。CloudFrontは、証明書がOrigin Domain Nameフィールドと正確に一致することを期待します。
これを修正するには、CloudFrontのオリジン設定に入力したドメイン名が、証明書のSubject Alternative Name (SAN)リストに含まれていることを確認してください。
3. NginxまたはApacheのチェーンを修正する
Nginxを使用している場合、証明書ファイルのみを指定するだけでは不十分です。フルチェーンが必要です。ssl_certificateディレクティブが、CAから提供されたfullchain.pem(または同等のファイル)を指していることを確認してください。
# Nginx設定例
server {
listen 443 ssl;
server_name origin.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# モダンで安全なプロトコルを使用する
ssl_protocols TLSv1.2 TLSv1.3;
}
4. ファイルの破損を確認する
手動のscp転送や、自動化スクリプトの失敗によって証明書ファイルが破損したケースを見たことがあります。テキストが正しく見えてもハンドシェイクが失敗する場合は、ファイルの整合性を確認してください。
私はよく、ToolCraftのHash Generatorを使用して、ローカルの証明書のSHA-256ハッシュとサーバー上のハッシュを比較します。ブラウザ内でローカルにファイルを処理するため、機密性の高いキーをサードパーティのサーバーにアップロードすることはありません。ハッシュが一致しない場合、アップロード中にファイルが壊れた可能性があります。
修正の検証方法
CloudFrontはデフォルトで502レスポンスを10秒間キャッシュします。サーバーの設定を変更した後は、数分待ってから再度テストしてください。curlを使用して、正しいHostヘッダーを強制しながらディストリビューションを直接テストできます:
curl -I https://your-id.cloudfront.net -H "Host: your-public-domain.com"
成功すると200 OKが返されます。依然として502が表示される場合は、X-Cacheヘッダーを確認してください。Error from cloudfrontと表示されている場合、依然としてハンドシェイクがボトルネックになっています。
最終チェックリスト
- CAによって署名されていますか?(自己署名証明書は許可されません)。
- チェーンは完全ですか?(中間証明書を含める必要があります)。
- ホスト名は一致していますか?(Origin Domain Nameが証明書のCN/SANと一致する必要があります)。
- ポートは開いていますか?(Security Groupで、CloudFrontのIP範囲からのポート443が許可されていることを確認してください)。

