2026年8月26日
2026年8月26日
Apacheのバーチャルホストでドメインごとに別のSSL証明書を使う方法(SNI)
Apache 2.4では、1つのIPアドレスと443番ポートのままで、ドメインごとに別のSSL証明書を使えます。ブラウザーがTLS接続の最初に接続先のホスト名を送る SNI(Server Name Indication) を使い、Apacheがそのホスト名に合う <VirtualHost> を選んで、その中の証明書を返すしくみです。2026年10月時点の主要なブラウザーやcurlはSNIに対応しているので、ドメインごとにIPアドレスを分ける必要はありません。
この記事では、Debian/Ubuntu系のApache(パッケージ名 apache2)を例に、example.com と example.net の2つのサイトにそれぞれ別の証明書を設定します。手順は2026年10月時点のDebian 13(Apache 2.4.69)で動作を確かめています。ドメイン名とパスは自分の環境に置き換えてください。
前提
- ApacheとOpenSSLがインストールされている
- 各ドメインの証明書(サーバー証明書と中間証明書)と秘密鍵がある
- 各ドメインのDNSが、このサーバーのIPアドレスを指している
Let's Encryptを使う場合は、certbot --apache がバーチャルホストの設定まで作れます。手順はCertbotでApacheにSSL証明書を設定する方法を参照してください。この記事では、証明書ファイルを自分で配置する場合の書き方を説明します。
手順1: mod_sslを有効にする
sudo a2enmod ssl
RHEL系(AlmaLinux、Rocky Linuxなど)では sudo dnf install mod_ssl でモジュールを入れます。設定ファイルの場所が /etc/httpd/conf.d/ になる点が異なり、a2enmod や a2ensite は使いません。
手順2: 証明書と秘密鍵を配置する
証明書は誰が読んでもよいファイルですが、秘密鍵はrootだけが読めるようにします。Debian/Ubuntuには、証明書用の /etc/ssl/certs/ と、鍵用の /etc/ssl/private/(ssl-certパッケージが入っていれば、rootとssl-certグループだけが入れる)があるので、それを使います。
sudo cp example.com.crt /etc/ssl/certs/example.com.crt
sudo cp example.com.key /etc/ssl/private/example.com.key
sudo cp example.net.crt /etc/ssl/certs/example.net.crt
sudo cp example.net.key /etc/ssl/private/example.net.key
sudo chmod 644 /etc/ssl/certs/example.com.crt /etc/ssl/certs/example.net.crt
sudo chmod 600 /etc/ssl/private/example.com.key /etc/ssl/private/example.net.key
証明書のファイルには、サーバー証明書のあとに中間証明書を続けて入れます(Let's Encryptの fullchain.pem と同じ形)。Apache 2.4.8以降では SSLCertificateFile に中間証明書を含められるので、古い記事にある SSLCertificateChainFile は使わなくてかまいません。中間証明書が抜けていると、一部の端末で証明書エラーになります。詳しくは中間証明書が足りないときのエラーと直し方を参照してください。
証明書に含まれるドメイン名と有効期限は、次のコマンドで確かめられます。
openssl x509 -in /etc/ssl/certs/example.com.crt -noout -subject -ext subjectAltName -dates
手順3: ドメインごとにバーチャルホストを書く
/etc/apache2/sites-available/example.com-ssl.conf を作ります。
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/public_html
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
同じように /etc/apache2/sites-available/example.net-ssl.conf を作ります。
<VirtualHost *:443>
ServerName example.net
ServerAlias www.example.net
DocumentRoot /var/www/example.net/public_html
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.net.crt
SSLCertificateKeyFile /etc/ssl/private/example.net.key
ErrorLog ${APACHE_LOG_DIR}/example.net-error.log
CustomLog ${APACHE_LOG_DIR}/example.net-access.log combined
</VirtualHost>
書くときの注意です。
SSLEngine onを各バーチャルホストに書きます。これが無いと、そのバーチャルホストではTLSが使われません。ServerNameとServerAliasに書いた名前が、証明書に含まれている必要があります。www付きでアクセスさせるなら、証明書にもwww.example.comが入っているか確かめます。ServerNameとServerAliasの使い分けはApacheのServerNameとServerAliasの設定を参照してください。<VirtualHost *:443>の書き方は、すべてのサイトでそろえます。あるサイトだけ<VirtualHost 192.0.2.10:443>のようにIPアドレスで書くと、そのIPアドレスへの接続では*:443のバーチャルホストが候補から外れ、意図しないサイトが表示されることがあります。
手順4: サイトを有効にして設定を読み込む
sudo a2ensite example.com-ssl example.net-ssl
sudo apache2ctl configtest
sudo systemctl reload apache2
apache2ctl configtest が Syntax OK を返してから読み込みます。エラーが出た場合は、表示された行を直してから再度実行します。
手順5: どの証明書が返っているか確かめる
まず、Apacheがバーチャルホストをどう認識しているかを表示します。
sudo apache2ctl -S
次のように、443番ポートに2つの名前ベースのバーチャルホストが並んでいれば設定は読み込まれています。default server に表示されたものが、どの名前にも当てはまらないアクセスを受け持つサイトです。
*:443 is a NameVirtualHost
default server example.com (/etc/apache2/sites-enabled/example.com-ssl.conf:1)
port 443 namevhost example.com (/etc/apache2/sites-enabled/example.com-ssl.conf:1)
alias www.example.com
port 443 namevhost example.net (/etc/apache2/sites-enabled/example.net-ssl.conf:1)
alias www.example.net
次に、ホスト名を指定して接続し、返ってきた証明書を確かめます。-servername で送るホスト名がSNIです。
echo | openssl s_client -connect 127.0.0.1:443 -servername example.net 2>/dev/null \
| openssl x509 -noout -subject -ext subjectAltName -dates
subject やSubject Alternative Nameに example.net が表示されれば、example.net 用の証明書が返っています。ドメインを変えて同じコマンドを実行し、それぞれの証明書が返ることを確かめます。
DNSを切り替える前にページの中身まで確かめたい場合は、curlの --resolve で接続先のIPアドレスを指定します。
curl -I --resolve example.net:443:192.0.2.10 https://example.net/
192.0.2.10 はこのサーバーのIPアドレスに置き換えます。
既定のサイトとSNIの注意点
- どの名前にも当てはまらないアクセス(設定していないドメイン名、IPアドレスでの直接アクセス、SNIを送らない古いクライアント)には、
apache2ctl -Sでdefault serverと表示されたバーチャルホストの証明書が返ります。これは、設定ファイルの読み込み順で最初に来たバーチャルホストです。 - Debian/Ubuntuに付属する
default-ssl.confを有効にしていると、ファイル名の順によってはこちらが既定のサイトになり、テスト用の自己署名証明書が返ることがあります。使っていなければsudo a2dissite default-sslで無効にします。 - 証明書の名前とアクセスしたドメイン名が合わない場合、ブラウザーは警告を出します。同じ1枚の証明書で複数のドメインを扱いたい場合は、SAN証明書やワイルドカード証明書を使う方法もあります(マルチドメイン(SAN)証明書の使い方)。
HTTPからHTTPSへ転送する
80番ポートのバーチャルホストを各ドメインに用意し、HTTPSへ転送します。
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
</VirtualHost>
example.net も同じように書きます。転送のくわしい書き方はApacheでHTTPSへのアクセスに統一する方法を参照してください。
うまくいかないとき
| 症状 | 確かめること |
|---|---|
| 別のドメインの証明書が返る | apache2ctl -S に目的のドメインが出ているか。ServerName/ServerAlias の綴り、<VirtualHost *:443> の書き方がそろっているか |
| Apacheが起動しない | apache2ctl configtest の表示。証明書と鍵のパス、鍵のパスフレーズの有無(パスフレーズ付きの鍵は起動時に入力を求められる) |
| 一部の端末だけ証明書エラーになる | 証明書ファイルに中間証明書が含まれているか |
| 設定を変えても反映されない | a2ensite で有効にしたか、systemctl reload apache2 を実行したか |
エラーの内容は、バーチャルホストに書いたエラーログ(この例では /var/log/apache2/example.com-error.log)と、全体のエラーログ /var/log/apache2/error.log に出ます。
sudo tail -n 50 /var/log/apache2/error.log
まとめ
Apache 2.4では、SNIによって1つのIPアドレスでもドメインごとに別の証明書を返せます。各ドメインの <VirtualHost *:443> に ServerName と SSLCertificateFile/SSLCertificateKeyFile を書き、apache2ctl configtest で確かめてから読み込みます。設定後は openssl s_client -servername でドメインごとに返る証明書を確かめ、どの名前にも当てはまらないアクセスに返る既定のサイトも把握しておきます。証明書の有効期限は証明書の情報を確認する方法の手順で定期的に確かめてください。