TL;DR
-
new.example.comのSSL設定は正しいのに、openssl s_clientやcurlで TLS 接続に失敗(packet length too longなど)。 - 原因は :443 の「最初に読み込まれた」VirtualHost(=デフォルトSSL vhost)が旧ドメイン(old) で、 そこに証明書が無かった ため。SNIが働く前に握手が崩壊していた。
- 旧ドメイン側にも暫定の証明書を当てたら即解決 。以後は SNI により new/old が正しく振り分けられる。
環境
- OS: RHEL 系(httpd / mod_ssl)
-
目的:
-
old.example.comは「常に new.example.com へリダイレクト」 -
new.example.comは本番サイトを提供
-
-
状況: old の :443 vhost が先に読み込まれていたが、 証明書が未設定 。
<VirtualHost *:443>
 ServerName old.example.com

 # ここが肝:TLSを受けない(証明書も置かない)

 # 以降は意味がないが、例として置いておく(実際はTLS成立前に失敗)
 RewriteEngine On
 RewriteRule ^(.*)$ https://new.example.com$1 [R=302,L]
</VirtualHost>

<VirtualHost *:443>
 ServerName new.example.com

 SSLEngine on
 SSLCertificateFile /etc/letsencrypt/live/new.example.com/fullchain.pem
 SSLCertificateKeyFile /etc/letsencrypt/live/new.example.com/privkey.pem
</VirtualHost>
症状
-
curlやopenssl s_clientの SNI 付き確認でも TLS 失敗:curl -I --resolve new.example.com:443:SERVER_IP https://new.example.com/
# curl: (35) TLS connect error: error:0A0000C6:SSL routines::packet length too long -
httpd -Sを見ると :80 も :443 も Default Server が old になっている
原因の正体(SNIとデフォルトSSL vhost)
-
Apache の :443 は、本来 TLS ハンドシェイク中に送られる SNI(ClientHello の
server_name)で該当 vhost を切り替え ます。 - ただし SNI を読む以前に TLS ハンドシェイクが成立しない場合 、SNI 判定まで到達できません。
-
今回は 最初に読み込まれた :443 の vhost(=デフォルトSSL vhost)が旧ドメインで、そこに証明書が無い/
SSLEngine Onで受けられない 状態だったため、 握手の入口で失敗 。結果として「SNI が合致しているはずなのに old の設定に飲まれる」ように見えました。 -
対策として 旧ドメイン側にも暫定証明書を付与 すると、まず TLS が成立 → SNI を読める →
new.example.comへ正しく振り分けられる、という順序で解消します。
どう解決したか(実施した対応)
✅ 旧ドメイン側にも暫定の証明書を当てる
- 正式発行前でもOK。 自己署名 でも、 new の証明書を一時流用 でも、まずは握手できればよいです。
# /etc/httpd/conf.d/110-old-ssl.conf
<VirtualHost *:443>
 ServerName old.example.com
 ServerAlias www.old.example.com
 SSLEngine on
 # 暫定の証明書(自己署名や一時SAN、もしくはとりあえず何かしら)
 SSLCertificateFile /etc/pki/tls/certs/old-temp.crt
 SSLCertificateKeyFile /etc/pki/tls/private/old-temp.key

 RewriteEngine On
 RewriteRule ^(.*)$ https://new.example.com$1 [R=302,L]
</VirtualHost>
自己署名の作成例:
sudo openssl req -x509 -nodes -newkey rsa:2048 \
 -keyout /etc/pki/tls/private/old-temp.key \
 -out /etc/pki/tls/certs/old-temp.crt \
 -days 30 -subj "/CN=old.example.com"
これで TLS ハンドシェイクが成立し、 SNI が正しく働く ようになるので、 new.example.com での接続は new vhost に振り分けられます。
まとめ
SNI は TLS が成立して初めて 読めます。
そのため、最初に受ける :443 vhost に証明書が無いと SNI 判定に到達せず、意図と異なる(あるいは失敗する)動作に見えることになります。
そこで旧側にも暫定証明書を当てることで TLS → SNI → vhost 切替の順序が正常化し、問題は解消するというわけです。
参考文献
この問題は、Apache公式Wiki「 SSL with Virtual Hosts Using SNI 」でも触れられています。
要点として、 SSL名ベースのVirtualHostでは、最初に定義された:443のvhostが初期ハンドシェイクに使われる ため、 そこにTLS1.0以上が許可され、証明書が載っていないとSNI情報を受け取る前に失敗 します。
結果として、SNIで一致しているはずのホストに切り替わらず、“最初のvhost”側の挙動(あるいはハンドシェイク失敗)に見えるという、まさに今回の事象と一致します。
該当箇所として、 「最初(デフォルト)のSSL vhostには少なくともTLSv1.0以上を許可する必要がある」「ホスト名が届かない場合は最初のvhostが使われる」 といった説明があります。