2026年9月25日
2026年9月25日
SNI を解除する方法:サーバー側の考え方・IISの設定・SNIなしで検証するコマンド
SNI(Server Name Indication)は、TLS のサーバー側で「機能ごと切る」スイッチがあるものではありません。「SNIを解除したい」という目的は、実際には「SNIに対応していないクライアントにも正しい証明書を返したい」「IIS のバインドで SNI 必須を外したい」「SNIを送らない接続でサーバーの挙動を確かめたい」のどれかであることがほとんどです。この記事では、それぞれの場面で何を設定すればよいか、そして解除する前に知っておくべき副作用を整理します。
SNI とは何か、なぜ「解除」が難しいのか
SNI は TLS の拡張機能(RFC 6066)で、ブラウザなどのクライアントが TLS ハンドシェイクの最初(ClientHello)に「接続したいホスト名」をサーバーへ伝える仕組みです。HTTP の Host ヘッダーは暗号化後にしか届かないため、1つの IP アドレスで複数のドメインを運用するサーバーは、SNI がないと「どのドメインの証明書を返すべきか」を判断できません。
つまり SNI は、サーバーが一方的に止めるものではなく、クライアントが送ってくる情報です。サーバー側でできるのは次のどちらかです。
- SNI が届かなかったとき(または知らない名前が届いたとき)に、どの証明書を返すかを決める
- SNI がない接続を拒否する、または受け入れる
現在の主要ブラウザ・OS・HTTPライブラリは SNI に対応しており、共有サーバーや CDN の多くは SNI を前提に動いています(2026年10月現在)。そのため「SNIをやめる」方向の設定は、多くの場合は不要で、むしろ複数ドメインの運用を難しくします。まず自分の目的がどれに当たるかを確認してください。
場面1:SNI 非対応のクライアントにも証明書を返したい
古い組み込み機器、更新されていない業務システム、一部の監視ツールなど、SNI を送らないクライアントから接続されると、サーバーは「既定の証明書」を返します。その証明書がアクセス先ドメインと一致しなければ、クライアント側では証明書の名前不一致エラーになります。これが「SNIのせいでつながらない」と言われる典型的な症状です。
Nginx:既定サーバーの証明書が返る
Nginx では、同じ listen アドレスとポートの中で default_server を付けた server ブロック(付けていなければ最初に定義されたもの)の証明書が、SNI のない接続に使われます。非対応クライアントを救う方法は主に2つです。
- 既定サーバーに、そのクライアントが接続するドメインを含む証明書を置く
- 非対応クライアントが使うドメインだけ、専用の IP アドレスで待ち受ける
# SNI のない接続はこのブロックの証明書を受け取る
server {
listen 443 ssl default_server;
server_name legacy.example.com;
ssl_certificate /etc/letsencrypt/live/legacy.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/legacy.example.com/privkey.pem;
root /var/www/legacy;
}
複数のドメインを非対応クライアントに見せたい場合は、それらを SAN(サブジェクト代替名)としてまとめた1枚の証明書を既定サーバーに置く方法があります。ただし、1枚の証明書にドメインを詰め込むと、証明書を見ればどのドメインが同居しているかが分かる、更新時にすべてのドメインの検証が通る必要がある、といった運用上の負担が増えます。
Apache:最初の VirtualHost が既定になる
Apache(mod_ssl)では、同じ IP とポートの <VirtualHost> のうち、設定ファイル上で最初に読み込まれたものが既定になります。Debian/Ubuntu 系で sites-enabled に並ぶファイル名の順序で既定が変わることがあるため、apachectl -S で実際の並びを確認してから証明書を配置してください。
# どの VirtualHost が 443 番の既定になっているかを表示
sudo apachectl -S
専用 IP を割り当てる場合の注意
SNI なしでドメインごとに別の証明書を返すには、ドメインごとに IP アドレスを分けるしかありません。IPv4 アドレスは貴重で、クラウドやVPSでは追加IPに費用や申請が必要なことが多いため、対象のクライアントを更新できないかを先に検討するのが現実的です。
場面2:IIS のバインドで「SNI 必須」を外したい
Windows Server の IIS には、HTTPS バインドの編集画面に「サーバー名表示を要求する」(英語版では Require Server Name Indication)というチェックボックスがあります(表記はバージョンや言語設定で異なる場合があります)。日本語の検索で「SNIを解除」と言われる操作は、多くの場合このチェックを外すことを指します。
チェックを外したバインドは「IPアドレス:ポート」単位のバインドになり、そのポートで SNI のない接続を受けたときに返す証明書として使われます。ここでつまずきやすいのが次の点です。
- 同じ IP とポートで SNI なしのバインドを持てるのは1つだけです。複数サイトでチェックを外すと、後から設定した証明書が前の設定を置き換えたり、意図しないサイトの証明書が返ったりします。
- バインドを変更したら、
netsh http show sslcertで実際に登録されている証明書の対応(IP:ポート単位か、ホスト名:ポート単位か)を確認すると、画面の表示と実体のずれに気づけます。
# HTTP.sys に登録された SSL 証明書のバインドを一覧表示(管理者権限)
netsh http show sslcert
本番サーバーでバインドを変更する前に、IIS の構成(applicationHost.config)をバックアップし、変更は利用者の少ない時間帯に行ってください。
場面3:SNI を送らずにサーバーの挙動を確かめたい
設定を変えたら、「SNI ありのとき」と「SNI なしのとき」にどの証明書が返るかを、手元から確認します。ブラウザでは SNI を止められないため、コマンドラインで試すのが確実な手段です。
openssl s_client で比べる
OpenSSL 1.1.1 以降の s_client は、-connect に指定したホスト名を SNI として自動で送ります。SNI なしを再現するには -noservername を付けます。
# SNI あり(example.com として接続)
echo | openssl s_client -connect 203.0.113.10:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -subject -ext subjectAltName
# SNI なし(既定の証明書が返る)
echo | openssl s_client -connect 203.0.113.10:443 -noservername 2>/dev/null \
| openssl x509 -noout -subject -ext subjectAltName
2つの結果で subject や SAN が違えば、サーバーは SNI によって証明書を切り替えています。SNI なしの結果が目的のドメインを含んでいなければ、非対応クライアントは名前不一致のエラーになります。なお -ext オプションは OpenSSL 1.1.1 以降の機能のため、古い環境では -text で全体を表示して SAN を探してください。
curl で比べる
curl は、URL のホスト部分が IP アドレスだと SNI を送りません(RFC 6066 で IP アドレスの送信が認められていないため)。これを利用すると、SNI なしで Host ヘッダーだけを指定した接続を再現できます。
# SNI なし・Host ヘッダーのみ指定(証明書の名前不一致は -k で無視して中身を確認)
curl -vk https://203.0.113.10/ -H "Host: example.com" -o /dev/null
# SNI あり・接続先 IP だけ固定(DNS を切り替える前の確認に便利)
curl -v --resolve example.com:443:203.0.113.10 https://example.com/ -o /dev/null
-v の出力に表示される証明書の subject を見比べてください。-k は検証を省くオプションなので、確認用途に限り、スクリプトや本番の連携処理には使わないでください。
Java など、クライアント側で SNI を止める設定
Java には jsse.enableSNIExtension というシステムプロパティがあり、false にすると SNI を送らなくなります。古い Java アプリが SNI 関連のエラー(サーバーが SNI 不一致の警告を返すケースなど)を出したときの切り分けで使われることがありますが、恒久対策にはせず、サーバー側の server_name や証明書を正しく直すことを優先してください。
場面4:リバースプロキシから HTTPS の上流へ SNI が届かない
「SNI を解除したら直った」という話の中には、実は逆で、プロキシが SNI を送っていなかったのが原因というケースがあります。Nginx の proxy_pass で HTTPS の上流(別サーバーや CDN 上のオリジン)に接続するとき、proxy_ssl_server_name の既定値は off で、上流に SNI を送りません。上流が SNI 前提の共有基盤だと、ハンドシェイクの失敗や別サイトの証明書が返る原因になります。
location / {
proxy_pass https://origin.example.net;
proxy_set_header Host origin.example.net;
# 上流へ SNI を送る
proxy_ssl_server_name on;
proxy_ssl_name origin.example.net;
# 上流の証明書も検証する(CA バンドルのパスは環境に合わせる)
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
}
設定後は sudo nginx -t で構文を確認し、問題がなければ sudo systemctl reload nginx で反映します。HAProxy など他のプロキシにも、上流へ送る SNI を指定する設定があります。オプション名はバージョンで変わることがあるため、使っている製品の公式ドキュメントで確認してください。
SNI がない接続を拒否したい場合
反対に「知らないホスト名や SNI なしの接続には証明書を見せたくない」という要件もあります。IP アドレスに直接アクセスされたときに、同居しているドメインの証明書が表示されるのを避けたい場合です。
- Nginx 1.19.4 以降では、既定サーバーに
ssl_reject_handshake on;を書くと、そのブロックに来た TLS 接続のハンドシェイクを拒否できます。 - Apache では
SSLStrictSNIVHostCheck onで、SNI を送らないクライアントの接続を拒否できます(設定する位置で効く範囲が変わります)。
# 未知のホスト名・SNI なしの接続を拒否する既定サーバー
server {
listen 443 ssl default_server;
ssl_reject_handshake on;
}
この設定を入れると、SNI 非対応のクライアントや IP 直打ちの監視は失敗するようになります。外形監視やロードバランサーのヘルスチェックが IP 指定で HTTPS を叩いていないかを事前に確認してください。
関連して知っておきたいこと:SNI は暗号化されていない
SNI は通常、平文で送られます。そのため通信経路上の機器は、通信内容が暗号化されていてもアクセス先のホスト名を知ることができます。これを隠す仕組みとして ECH(Encrypted Client Hello)があり、一部のブラウザや CDN で対応が進んでいます(2026年10月現在)。対応状況は製品ごとに変わるため、利用する場合は CDN やブラウザの公式情報で確認してください。SNI を「解除」してもホスト名の秘匿にはならず、接続そのものが成り立たなくなる場合が多い点は押さえておきましょう。
作業前のチェックリスト
- 目的はどれか(非対応クライアント対策/IIS のバインド変更/検証/プロキシの修正/拒否)を決めたか
- 変更前の設定ファイル(Nginx・Apache の設定、IIS の構成)をバックアップしたか
- ステージングや検証用の IP で、SNI あり・なしの両方を
openssl s_clientで確認したか - 監視やヘルスチェック、外部連携が SNI なしで接続していないか確認したか
まとめ
- SNI はクライアントが送るホスト名の情報で、サーバー側で機能ごと止める設定は一般的ではありません。
- SNI 非対応クライアントには「既定サーバーの証明書」が返るので、そこに必要なドメインを含めるか、専用 IP を割り当てます。
- IIS の「SNI 必須」を外したバインドは IP:ポート単位で1つだけ。
netsh http show sslcertで実体を確認します。 - 検証は
openssl s_client -noservernameと、IP 指定の curl で SNI あり・なしを比べます。 - プロキシ経由で不具合が出るときは、上流へ SNI を送る設定(Nginx の
proxy_ssl_server_name on)が抜けていないかを先に確認してください。