【Apache SSL講座 第6回】「繋がらない」を解決!中間証明書チェーンの仕組みとトラブルシューティング完全ガイド

「PCだと見れるのに、スマホだと警告が出るんです…」

こんにちは!「LINUX工房」管理人の「リナックス先生」です。
前回は、バーチャルホストとSNIを使って、1台のサーバーで複数のHTTPSサイトを運用する方法を学びました。

さて、SSLサーバー構築の現場で、最も頻繁に発生し、かつエンジニアを悩ませる「怪奇現象」があります。
それは、「自分のパソコン(Chrome)では鍵マークが出て正常に見れるのに、上司のスマホ(iPhone)や、古いAndroid端末で見ると『安全ではありません』という警告が出る」という現象です。

コウ君

先生、それ昨日ありました!
設定は完璧なはずなのに、お客様から「スマホでアクセスできないぞ!」ってクレームが来て…。
でも僕のPCからは普通に見れるから「お客様の環境のせいじゃないですか?」って言いそうになりました。
あれ、サーバー側の設定ミスなんですか?

リナックス先生

危ない危ない!それを言っていたら大事故だったわよ、コウ君。
その現象の犯人は99%、「中間証明書(チェーン)」の設定漏れよ。
PCのブラウザは賢いから、足りない情報を勝手に補完してくれるけど、スマホやプログラムは正直だからエラーを吐くの。
今回は、この見えにくい「信頼の鎖」を正しくつなぐ方法をマスターしましょう!

本記事では、SSL証明書の「階層構造」を深く理解し、openssl コマンドを使って「チェーン切れ」を診断・修復するプロのトラブルシューティング技術を解説します。

🔒 Apache SSL/TLS 完全攻略講座(バックナンバー)

現在地:【第6回】「繋がらない」を解決!中間証明書チェーンとトラブルシューティング


第1章:証明書の「階層構造(Chain of Trust)」とは?

SSL証明書は、1枚の紙切れではありません。
「信頼」をつなぐための階層構造(チェーン)になっています。

3つの登場人物

種類 役割 場所
ルート証明書
(Root CA)
「神」のような存在。
全ての信頼の源。OSやブラウザに最初からインストールされている。
クライアント端末
(PC/スマホ)
中間証明書
(Intermediate CA)
「仲介業者」。
ルート証明書から信頼を委任され、サーバー証明書を発行する。
Webサーバー
(あなたが設置する)
サーバー証明書
(Server Cert)
「あなたのサイト」。
ドメイン名(example.com)が記載された証明書。
Webサーバー
(あなたが設置する)

検証の流れ(鎖をつなぐ)

ブラウザがあなたのサイト(サーバー証明書)にアクセスした時、以下の手順で検証を行います。

  1. サーバー証明書を見る: 「発行者は『GlobalSign Intermediate CA』か。じゃあその人の証明書を見せて。」
  2. 中間証明書を見る: 「発行者は『GlobalSign Root CA』か。じゃあその人の証明書は…」
  3. ルート証明書を見る: 「あ、この『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 ... コマンドを実行し、01 が表示されるようになれば修正完了です!

💡 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.

原因と対策

これらは「クライアント(プログラム)側」が持っているルート証明書ストアに、対象のルート証明書が入っていないことが原因です。

  1. 中間証明書の確認: まずは本記事の第4章に従い、サーバー側でチェーンが正しく送られているか確認してください(これが原因の9割です)。
  2. ルート証明書の更新: サーバー側が正しいなら、クライアント側の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」で自分専用環境

おすすめVPSを見る

サーバー知識を年収に
「ITエンジニア転職」

転職エージェントを見る

コメント