2026年9月20日

2026年9月20日

CSP と SNI の関係|別の層の仕組みが交わる場面とトラブルの見分け方

CSP(Content Security Policy)と SNI(Server Name Indication)は、名前が似た略語として並べられることがありますが、直接の関係はありません。CSP は HTTP の応答ヘッダーでブラウザに「どこからリソースを読み込んでよいか」を伝える仕組み、SNI は TLS の接続開始時に「どのホスト名に接続したいか」をサーバーに伝える仕組みで、働く層もタイミングも異なります。ただ、1台のサーバーで複数のドメインを運用したり CDN を使ったりすると、両者が絡んだように見えるトラブルが起きます。この記事では、それぞれの役割を整理し、交わる場面と見分け方を説明します。

それぞれの役割

SNI:TLS で接続先のホスト名を伝える

HTTPS の通信では、HTTP のやり取りより先に TLS のハンドシェイクが行われます。1つの IP アドレスで複数のドメインを運用していると、サーバーはどのドメインの証明書を返せばよいか分かりません。そこでクライアントは、最初のメッセージ(ClientHello)に SNI としてホスト名を含めて送ります。サーバーはそれを見て、対応する証明書と設定を選びます。

Nginx であれば、SNI の値と server_name が一致する server ブロックが選ばれ、そのブロックの ssl_certificate が使われます。一致するものが無い場合は既定のサーバー(default_server)が使われます。

なお、SNI は通常は暗号化されずに送られるため、通信経路から接続先のホスト名が見える状態です。これを暗号化する ECH(Encrypted Client Hello)という仕組みがあり、ブラウザや CDN での対応が進んでいます。対応状況は(2026年10月現在)も変化している途中なので、利用するサービスの情報を確認してください。

CSP:ブラウザに読み込みの許可範囲を伝える

CSP は TLS の接続が確立し、HTTP の応答が返ってきた後に働きます。Content-Security-Policy ヘッダーで、スクリプト、スタイル、画像、フォント、接続先などを、どのオリジンから読み込んでよいかをブラウザに指示します。主な目的は XSS などで差し込まれた不正なスクリプトの実行を抑えることです。

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; img-src 'self' https://cdn.example.com data:

CSP はブラウザ側の判断材料であり、通信を暗号化したり、接続先のホスト名を隠したりする機能はありません。SNI で見えるホスト名を CSP で守ることはできず、逆に SNI の設定で CSP の役割を代替することもできません。

両者が交わる3つの場面

1. CSP で許可したホストに TLS で接続できない

CSP で https://cdn.example.com を許可しても、ブラウザがそのホストに接続する段階で証明書が合わなければ、リソースは読み込まれません。共有 IP のサーバーで cdn.example.com の server ブロックが無い、あるいは証明書に名前が含まれていないと、SNI で一致するブロックが見つからず、別のドメインの証明書が返って接続エラーになります。

画面上は「画像やスクリプトが読み込まれない」という同じ症状でも、原因は CSP ではなく TLS です。開発者ツールのコンソールで見分けられます。

  • CSP が原因なら、コンソールにポリシー違反のメッセージ(Content Security Policy のディレクティブに違反したため読み込みを拒否した、という趣旨)が出る
  • TLS が原因なら、ネットワークタブでそのリクエストが net::ERR_CERT_COMMON_NAME_INVALID などの証明書エラーで失敗している

2. SNI の選択ミスで、別サイトの CSP が返る

同じサーバーで複数サイトを運用し、server ブロックごとに CSP を設定している場合、SNI と server_name が一致しないアクセスは既定のサーバーに届きます。その結果、別のサイト向けの証明書とともに、別のサイト向けの CSP が返ることがあります。サイト A の CSP がサイト B のドメインを許可していないため、表示が崩れる、という形で現れます。

証明書エラーが先に出ることが多いものの、検証ツールで証明書エラーを無視して取得した場合や、意図せず同じ証明書に複数の名前が入っている場合は、CSP の不一致だけが目立つことがあります。

3. upgrade-insecure-requests で HTTPS 化を強制したとき

CSP の upgrade-insecure-requests は、ページ内の http:// のリソースを https:// で読み込むようブラウザに指示します。混在コンテンツ対策として便利ですが、読み込み先のホストが HTTPS に対応していない、あるいは SNI に対応した正しい証明書を返さないと、そのリソースは読み込めなくなります。有効にする前に、外部の画像や埋め込みの読み込み先が HTTPS で正しく応答するかを確認してください。

切り分けの手順

読み込みのエラーが CSP と TLS のどちらによるものかは、次の順で確認すると判断できます。

まず、対象ホストが SNI に対して正しい証明書を返すかを確認します。-servername が SNI の値です。

openssl s_client -connect cdn.example.com:443 -servername cdn.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -ext subjectAltName -dates

表示された名前(subjectAltName)に対象ホストが含まれていなければ、TLS 側の問題です。証明書の追加や server ブロックの設定を見直します。

次に、ページが返している CSP ヘッダーを確認します。

curl -sI https://example.com/ | grep -i content-security-policy

DNS を切り替える前の新サーバーなど、特定の IP に対して確認したい場合は --resolve を使うと、SNI とホスト名を保ったまま接続先だけを変えられます。

curl -sI --resolve example.com:443:203.0.113.10 https://example.com/ | grep -i content-security-policy

Nginx での設定例と注意点

複数サイトを1台で運用するときは、サイトごとに証明書と CSP をそろえて書きます。

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    add_header Content-Security-Policy "default-src 'self'; img-src 'self' https://cdn.example.com data:" always;
}

http2 on; は Nginx 1.25.1 以降の書き方で、それより古い版では listen 443 ssl http2; を使います。また、add_header は location ブロックで1つでも書くと、server ブロックで書いたものが引き継がれなくなります。特定の location だけ CSP が消える場合は、この継承の仕様を疑ってください。

WordPress 側で CSP を出したい場合は、子テーマや mu-plugin で send_headers アクションにヘッダー送信の処理を登録する方法もあります。ただし、サーバーとプラグインの両方で CSP を出すとヘッダーが重複し、ブラウザは両方のポリシーを満たす読み込みだけを許可するため、意図より厳しくなります。どこで設定するかを1か所に決めてください。いきなり本番で適用せず、Content-Security-Policy-Report-Only で違反を観察してから切り替える手順が安全です。

まとめ

  • SNI は TLS の接続時にホスト名を伝える仕組み、CSP は HTTP 応答でブラウザに読み込みの許可範囲を伝える仕組みで、直接の関係はない
  • CSP で許可したホストでも、SNI に合った証明書が無ければ TLS の段階で読み込みに失敗する
  • 共有 IP で SNI が一致しないと、既定のサーバーの証明書と CSP が返り、別のトラブルに見えることがある
  • コンソールのメッセージ、openssl s_client -servername、curl -sI で、CSP と TLS のどちらが原因かを切り分ける

お気軽にご相談ください

お見積りへ お問い合わせへ
▲