「PCだと見れるのに、スマホだと警告が出るんです…」
こんにちは!「LINUX工房」管理人の「リナックス先生」です。
前回は、バーチャルホストとSNIを使って、1台のサーバーで複数のHTTPSサイトを運用する方法を学びました。
さて、SSLサーバー構築の現場で、最も頻繁に発生し、かつエンジニアを悩ませる「怪奇現象」があります。
それは、「自分のパソコン(Chrome)では鍵マークが出て正常に見れるのに、上司のスマホ(iPhone)や、古いAndroid端末で見ると『安全ではありません』という警告が出る」という現象です。
先生、それ昨日ありました!
設定は完璧なはずなのに、お客様から「スマホでアクセスできないぞ!」ってクレームが来て…。
でも僕のPCからは普通に見れるから「お客様の環境のせいじゃないですか?」って言いそうになりました。
あれ、サーバー側の設定ミスなんですか?
危ない危ない!それを言っていたら大事故だったわよ、コウ君。
その現象の犯人は99%、「中間証明書(チェーン)」の設定漏れよ。
PCのブラウザは賢いから、足りない情報を勝手に補完してくれるけど、スマホやプログラムは正直だからエラーを吐くの。
今回は、この見えにくい「信頼の鎖」を正しくつなぐ方法をマスターしましょう!
本記事では、SSL証明書の「階層構造」を深く理解し、openssl コマンドを使って「チェーン切れ」を診断・修復するプロのトラブルシューティング技術を解説します。
🔒 Apache SSL/TLS 完全攻略講座(バックナンバー)
現在地:【第6回】「繋がらない」を解決!中間証明書チェーンとトラブルシューティング
- 【第1回】Webサーバーの常時SSL化!TLSの仕組みと自己署名証明書の作成
- 【第2回】無料証明書のスタンダード!Let’s Encrypt (Certbot) の導入と自動更新
- 【第3回】企業サイトならこれ!有料証明書(JPRS/GlobalSign)のCSR生成とインストール
- 【第4回】セキュリティ強度を上げる!プロトコル(TLS1.3)と暗号スイートの最適化
- 【第5回】1つのIPで複数ドメイン!バーチャルホストでのSSL設定とSNIの仕組み
- 【第6回】「繋がらない」を解決!中間証明書チェーンとトラブルシューティング
- 【第7回】運用を楽にする!証明書期限監視と更新プロセスの自動化
- 【第8回】大規模構成!ロードバランサ連携とHTTP/2・HTTP/3 (QUIC) 対応
第1章:証明書の「階層構造(Chain of Trust)」とは?
SSL証明書は、1枚の紙切れではありません。
「信頼」をつなぐための階層構造(チェーン)になっています。
3つの登場人物
| 種類 | 役割 | 場所 |
|---|---|---|
| ルート証明書 (Root CA) |
「神」のような存在。 全ての信頼の源。OSやブラウザに最初からインストールされている。 |
クライアント端末 (PC/スマホ) |
| 中間証明書 (Intermediate CA) |
「仲介業者」。 ルート証明書から信頼を委任され、サーバー証明書を発行する。 |
Webサーバー (あなたが設置する) |
| サーバー証明書 (Server Cert) |
「あなたのサイト」。 ドメイン名(example.com)が記載された証明書。 |
Webサーバー (あなたが設置する) |
検証の流れ(鎖をつなぐ)
ブラウザがあなたのサイト(サーバー証明書)にアクセスした時、以下の手順で検証を行います。
- サーバー証明書を見る: 「発行者は『GlobalSign Intermediate CA』か。じゃあその人の証明書を見せて。」
- 中間証明書を見る: 「発行者は『GlobalSign Root CA』か。じゃあその人の証明書は…」
- ルート証明書を見る: 「あ、この『GlobalSign Root CA』なら、私のスマホの中に最初から入ってるわ!信頼できる!」
この鎖(チェーン)が途切れることなくルートまで辿り着けた時、初めて「安全」とみなされます。
逆に、サーバーが「中間証明書」を送り忘れると、「サーバー証明書は持ってるけど、発行者が誰だか分からない(鎖が切れた)」状態になり、警告が出るのです。
第2章:なぜPCでは見れてしまうのか?(AIA Fetching)
「中間証明書を設定し忘れたのに、私のWindows(Chrome)では見れましたよ?」
これがトラブル発見を遅らせる最大の罠です。
最近のPC用ブラウザ(特にChromeやEdge)は非常に賢く、中間証明書が欠けていると、証明書の中に書かれている「AIA (Authority Information Access)」という情報を元に、インターネット上から勝手に中間証明書を探してダウンロード(補完)してくれます。
しかし、以下の環境ではこの「補完機能」が働きません。
- モバイル端末(iPhone / Android): 通信量削減のため、余計なダウンロードをしない。
- 古いブラウザ: 機能がない。
- プログラム(curl, Java, Python, PHPなど): 厳密に検証するため、チェーンが切れていれば即エラーにする。
だからこそ、「PCで見れるからOK」は絶対に禁物なのです。
第3章:チェーン切れの診断方法(OpenSSLコマンド)
ブラウザで確認するのは確実ではありません。
サーバー内部から openssl コマンドを使って、客観的な事実を確認しましょう。
診断コマンド(s_client)
以下のコマンドを実行します。example.com はあなたのドメインに置き換えてください。
openssl s_client -connect example.com:443 -showcerts
結果の見方(成功パターン)
Certificate chain というセクションに、以下のように 0, 1, 2… と証明書が連なっているのが正解です。
Certificate chain 0 s:CN = www.example.com (あなたのサーバー証明書) i:CN = Let's Encrypt R3 (発行した中間CA) 1 s:CN = Let's Encrypt R3 (中間証明書) i:CN = ISRG Root X1 (ルートCA)
s:(Subject) = 証明書の持ち主i:(Issuer) = 発行者
0番のIssuer(発行者)が、1番のSubject(持ち主)と一致していることが重要です。
結果の見方(失敗パターン)
Certificate chain 0 s:CN = www.example.com i:CN = Let's Encrypt R3 (ここで終わっている)
このように 0 番しか表示されない場合、「中間証明書が送られていない(チェーン切れ)」状態です。
また、最後の判定結果に以下のようなエラーが出ます。
Verify return code: 21 (unable to verify the first certificate)
第4章:Apacheでの正しい中間証明書の設定
チェーン切れが確認できたら、Apacheの設定を修正します。
ここでのポイントは、「Apacheのバージョンによって書き方が違う」ということです。
古い書き方(Apache 2.4.7 以前)
以前は、中間証明書を専用のディレクティブで指定していました。
SSLCertificateFile /etc/pki/tls/certs/server.crt SSLCertificateKeyFile /etc/pki/tls/private/server.key SSLCertificateChainFile /etc/pki/tls/certs/intermediate.crt <-- これ
AlmaLinux 9 の Apache (2.4.5x) でもこの書き方は動作しますが、現在は推奨されていません(Deprecated)。
現在の書き方(Apache 2.4.8 以降)
現在は、「サーバー証明書と中間証明書を1つのファイルに結合する」のが標準です。SSLCertificateFile だけで完結します。
手順1:ファイルの結合(フルチェーン証明書の作成)
サーバー証明書(server.crt)と中間証明書(intermediate.crt)を、cat コマンドで結合します。
順番が重要です。「サーバー証明書」が先、「中間証明書」が後です。
# 書式: cat [サーバー証明書] [中間証明書] > [結合ファイル] sudo cat /etc/pki/tls/certs/server.crt /etc/pki/tls/certs/intermediate.crt > /etc/pki/tls/certs/server-chain.crt
手順2:Apacheの設定変更
/etc/httpd/conf.d/ssl.conf(またはバーチャルホスト設定)を編集します。
# 結合したファイルを指定 SSLCertificateFile /etc/pki/tls/certs/server-chain.crt # 秘密鍵はそのまま SSLCertificateKeyFile /etc/pki/tls/private/server.key # ChainFileの行は削除またはコメントアウト # SSLCertificateChainFile ...
手順3:再起動と確認
sudo systemctl restart httpd
再度 openssl s_client ... コマンドを実行し、0 と 1 が表示されるようになれば修正完了です!
💡 Certbotを使っている場合
Let’s Encrypt (Certbot) を使っている場合は、自動的に fullchain.pem という結合済みファイルが生成され、設定されています。
もしCertbotを使っているのにチェーン切れが起きている場合は、設定ファイルが cert.pem(証明書単体)を指していないか確認し、fullchain.pem に書き換えてください。
第5章:その他の「警告が出る」原因(Mixed Content)
証明書チェーン以外で、最も多い警告の原因が「Mixed Content(混合コンテンツ)」です。
現象
HTTPSでアクセスしているのに、ブラウザのアドレスバーに「!」マークが出たり、鍵マークが崩れていたりする。
原因
HTTPSのページの中に、HTTP(暗号化されていない)リソースが埋め込まれています。
例えば、画像のURLが <img src="http://example.com/image.jpg"> となっている場合です。
調査と対策
Chromeのデベロッパーツール(F12)を開き、「Console」タブを確認してください。
赤字または黄色字で “Mixed Content: The page at ‘https://…’ was loaded over HTTPS, but requested an insecure image…” といったエラーが出ています。
対策:
HTMLソース内の http:// を全て https:// に書き換えるか、ドメインを省略した相対パス(src="/image.jpg")またはプロトコル相対パス(src="//example.com/image.jpg")に修正してください。
第6章:プログラム(API)連携時のエラー
Webブラウザではなく、JavaやPython、curlなどのプログラムからAPIとしてアクセスした時にエラーになるケースです。
エラー例
- Java:
PKIX path building failed: unable to find valid certification path to requested target - curl:
curl: (60) Peer's Certificate issuer is not recognized.
原因と対策
これらは「クライアント(プログラム)側」が持っているルート証明書ストアに、対象のルート証明書が入っていないことが原因です。
- 中間証明書の確認: まずは本記事の第4章に従い、サーバー側でチェーンが正しく送られているか確認してください(これが原因の9割です)。
- ルート証明書の更新: サーバー側が正しいなら、クライアント側のOSやJavaのルート証明書リストが古すぎます。
yum update ca-certificates(Linux) やJavaのアップデートを行ってください。
まとめ:信頼の鎖を繋ぐのはエンジニアの責任
お疲れ様でした!
今回は、少し難解な「証明書チェーン」とトラブルシューティングについて解説しました。
今回の重要ポイント:
- PCで見れてもスマホで見れない原因は「中間証明書」にある。
openssl s_clientコマンドでチェーン切れを客観的に診断する。- Apache 2.4.8以降は、証明書ファイルを結合して
SSLCertificateFileに設定する。 - Mixed Content(http混在)も警告の主要因。
これで、あなたのWebサーバーはどんなデバイスからアクセスされても、正しく安全であることを証明できるようになりました。
さて、HTTPS化して運用を始めると、次に怖いのが「証明書の有効期限切れ」です。
Let’s Encryptなら自動更新されますが、有料証明書や、何らかのエラーで自動更新が止まっていた場合、ある日突然サイトが見られなくなります。
次回、第7回は「運用を楽にする!証明書期限監視と更新プロセスの自動化」です。
証明書の期限が近づいたらSlackやメールに通知を送る監視スクリプトの作成や、更新時のApache再起動を安全に行うテクニックを紹介します。
「うっかり失効」を防ぐための守りの技術、お楽しみに!
▼ エンジニアとしてのキャリアを加速させる ▼
トラブルシューティング実験
「VPS」で自分専用環境
サーバー知識を年収に
「ITエンジニア転職」

コメント