HSTS Preload設定手順|nginx・Cloudflare実装とhstspreload.org申請ガイド

HSTS HTTPS セキュリティ HTTPヘッダー nginx Cloudflare
結論
  • HSTS Preloadは、ブラウザに組み込まれた静的リストにより初回訪問からHTTPSを強制する仕組みだ。
  • 実装の核心は Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadHTTPSレスポンスだけに返すこと、そして includeSubDomains 配下の全サブドメインがHTTPS対応済みであること。
  • 登録はほぼ不可逆なので、hstspreload.org への申請は段階的導入と棚卸しを終えてから行う。

HSTS(HTTP Strict Transport Security)とは

HTTPSサイトでも、ユーザーがアドレスバーに http:// と入力したり、公共Wi-Fi上で攻撃者が接続をHTTPへダウングレードしたりすると、最初の1リクエストだけ平文で流れる隙間がある。攻撃者はこの瞬間にSSLストリッピング(HTTPSを装わずHTTPのまま中継する攻撃)を仕掛けられる。

HSTSはサーバーが返す Strict-Transport-Security レスポンスヘッダーで、ブラウザに「このホストには max-age 秒間、HTTPではなくHTTPSで接続せよ」と指示する仕組みだ。一度HTTPSでアクセスしたブラウザは、以降 http://example.com を入力しても、ネットワークにHTTPリクエストを送る前に内部で https://example.com に書き換える。

代表的なヘッダー:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
ディレクティブ意味
max-ageHSTSポリシーの有効期間(秒)。Preload要件は 31536000(1年)以上
includeSubDomains*.example.com 配下のサブドメインにも適用。Preload申請では必須
preload運営者がPreloadリストへの登録を意図していることを示す。申請手続きは別途必要
初回アクセス問題

通常のHSTSだけでは「まだHSTSを知らないブラウザの初回訪問」はHTTPのまま到達しうる。Preloadはこの穴を塞ぐが、副作用も大きい。

Preloadリストが解決する「初回アクセス問題」

HSTS Preload は、Chrome・Chromium系・Firefox・Safari・Edgeなど主要ブラウザに静的に組み込まれたドメインリストだ。リストに載った example.com へは、ユーザーが一度も訪問したことがなくても、ブラウザは最初からHTTPSのみで接続する。

リストの運用窓口は hstspreload.org。要件を満たしたドメインを申請すると、審査のうえ各ブラウザベンダーへ配布され、次期ブラウザリリースに反映される。反映まで数週間〜数ヶ月かかることもある。

観点 通常HSTS / Preload
適用タイミング 通常: 初回HTTPS訪問後 / Preload: 初回から
必要なヘッダー 通常: max-age のみ可 / Preload: preload + includeSubDomains + max-age≥1年
サブドメイン Preloadは includeSubDomains 必須。未対応サブドメインがあると申請不可または運用事故
取り消し 通常: max-age切れで自然消滅 / Preload: リスト更新まで数ヶ月〜数年
主な防御 両方: SSLストリッピング・プロトコルダウングレード攻撃の抑制

Preload登録の公式要件(hstspreload.org)

hstspreload.org が定める申請要件は次のとおり。サイト上の自動チェッカーがこれらを検証する。

  1. apexドメインexample.com)がHTTPSで応答する
  2. wwwサブドメインwww.example.com)がHTTPSで応答する(wwwなしのみのサイトでも、wwwへのリダイレクトまたは同等のHTTPS応答が必要)
  3. HTTP(平文)アクセスは恒久的な301リダイレクトでHTTPSへ誘導する(302・307・メタリフレッシュ・JavaScriptリダイレクトは不可)
  4. HTTPSレスポンスに Strict-Transport-Security ヘッダーがあり、max-age が 31536000 以上
  5. ヘッダーに includeSubDomains が含まれる
  6. ヘッダーに preload ディレクティブが含まれる
  7. すべてのサブドメインがHTTPS対応であること(includeSubDomains の意味上の前提)

申請前の確認コマンド:

# HTTPSでHSTSヘッダーを確認(preload を含むこと)
curl -sI https://example.com | grep -i strict-transport-security

# HTTPが301でHTTPSへ飛ぶこと
curl -sI http://example.com | grep -iE "^(HTTP/|location:)"

# www側も同様に確認
curl -sI https://www.example.com | grep -i strict-transport-security
curl -sI http://www.example.com | grep -iE "^(HTTP/|location:)"

期待される出力例:

strict-transport-security: max-age=31536000; includeSubDomains; preload
HTTP/1.1 301 Moved Permanently
location: https://example.com/

nginxでのHSTS実装(本番設定例)

nginxでは HTTPS(443)の server ブロックだけ にHSTSを付与する。always フラグを付けないと、4xx/5xxレスポンスでヘッダーが落ちる場合があるため、Preload申請前は always を推奨する。

# HTTP → HTTPS 恒久リダイレクト(80番)
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    # HSTSヘッダーはここには付けない
    return 301 https://$host$request_uri;
}

# HTTPS(443番)— HSTSはここだけ
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Preload申請用の完全形
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    root /var/www/example.com;
    index index.html;
}

サブドメインごとに server ブロックを分ける場合、includeSubDomains を宣言している以上、リストアップした全ホストでHTTPSが有効である必要がある。api.example.com だけ別サーバーでHTTPのまま、という状態はPreload申請前に解消する。

段階導入時は max-age を短く始める:

# 検証フェーズ(5分)。問題なければ 86400 → 31536000 へ延長
add_header Strict-Transport-Security "max-age=300" always;

設定反映後は nginx -t && systemctl reload nginx で構文チェックとリロードを行う。

CloudflareでのHSTS設定と注意点

Cloudflare経由のサイトは、ダッシュボード SSL/TLS → Edge Certificates → HTTP Strict Transport Security (HSTS) からEdgeレスポンスにHSTSを付与できる。

設定項目Preload申請時の推奨値
Enable HSTSON
Max Age12 months(31536000秒)以上
Include subdomainsON(必須)
PreloadON(必須)
No-Sniff header任意(X-Content-Type-Options と併用推奨)

Cloudflare有効時、ブラウザが受け取るレスポンス例:

strict-transport-security: max-age=31536000; includeSubDomains; preload

注意すべき点:

  • Always Use HTTPS(SSL/TLS → Edge Certificates)をONにし、HTTPリクエストがEdgeで301リダイレクトされることを確認する
  • Cloudflareとオリジンnginxの両方でHSTSを返すと、値が異なる場合にどちらが有効か分かりにくい。Edgeかオリジンのどちらか一方に集約する
  • Flexible SSL(Cloudflare↔ブラウザはHTTPS、Cloudflare↔オリジンはHTTP)はHSTSと相性が悪い。Full(Strict)を推奨
  • オレンジクラウド(プロキシ)がOFFのサブドメインはCloudflareのHSTSが効かない。includeSubDomains 下でHTTPが残ると申請要件を満たせない

Cloudflare Transform Rulesで手動付与する場合(ダッシュボード設定の代替):

# Response Header の Set 操作
Strict-Transport-Security = max-age=31536000; includeSubDomains; preload

includeSubDomainsのリスクとサブドメイン棚卸し

includeSubDomainsexample.com だけでなく anything.example.com すべてにHSTSを及ぼす。Preload申請では省略できないため、申請前にサブドメインの完全な棚卸しが必須だ。

典型的な事故パターン:

サブドメイン想定外の影響
staging.example.com開発環境がHTTPのままだと、Preload後に開発者がアクセス不能になる
mail.example.comレガシーメールサーバーがHTTP管理画面のままだと到達不能
old-api.example.com証明書期限切れのサブドメインは、ブラウザが接続を拒否し続ける
dev.example.com社内のみのHTTPサービスが外部からもHTTPS強制される

棚卸し手順:

1

DNSゾーンの全レコード(A/AAAA/CNAME)をエクスポートし、*.example.com のホスト名をリスト化する

2

各ホストに対し curl -sI https://<host>/ で証明書エラーがないか確認する

3

HTTPのみのホストは301でHTTPSへリダイレクトするか、サービスを廃止・別ドメインへ移す

4

使わないサブドメインのDNSレコードを削除し、第三者が取得できないようにする

5

includeSubDomains 付きの短い max-age で1週間様子を見てから preload を付与する

別ドメインへの逃げ道はない

includeSubDomainsexample.com のサブドメインにのみ 効く。example-staging.com のような別ドメインは対象外だ。検証環境はサブドメインではなく別ドメインに置くのが安全な設計パターンだ。

リダイレクトループの原因とデバッグ手順

HSTS導入時に最も混乱しやすいのがリダイレクトループだ。ブラウザが「HTTPSで接続しろ」と記憶しているのに、サーバー側がHTTPへ戻そうとしたり、証明書とホスト名が一致しない場合に発生する。

よくある原因:

  1. HTTPレスポンスにHSTSヘッダーを付けている — 平文接続でもブラウザがHTTPSを強制し、設定ミスと組み合わさってループする
  2. Flexible SSL + オリジンHTTPリダイレクト — EdgeはHTTPS、オリジンは「HTTPSで来たリクエストをHTTPへ」と誤設定
  3. 証明書のCN/SAN不一致www.example.com の証明書がなく、HTTPS接続が失敗→リトライの繰り返し
  4. ロードバランサとオリジンの二重リダイレクト — LBがHTTPS終端し、オリジンへHTTP転送、オリジンが再びHTTPSへ301
  5. HSTS preload 済み + 一時的なHTTP運用への切り戻し — ブラウザがHTTPを拒否するため「戻せない」状態に見える

デバッグ手順:

# 1. リダイレクトチェーンを追跡(ループ検出)
curl -sI -L --max-redirs 10 http://example.com

# 2. HTTPSレスポンスのステータスとHSTSを確認
curl -sI https://example.com

# 3. 証明書のSANを確認
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -ext subjectAltName

ブラウザ側の確認:

  • Chrome: chrome://net-internals/#hsts → 「Query HSTS/PKP domain」にドメインを入力し、ポリシー有無を確認
  • 削除テスト: 同画面の「Delete domain security policies」でローカルのHSTSキャッシュを消し、挙動を再現する(Preloadリスト自体は消えない)
HTTPにHSTSを付けない

return 301 https://... する HTTPの server ブロックに add_header Strict-Transport-Security を書かない こと。HTTPSブロックだけに書くのが鉄則だ。

ループが疑われるときの切り分け表:

症状疑う箇所
curl は正常、ブラウザだけループブラウザのHSTSキャッシュまたはPreloadリスト
www のみ失敗www用証明書・server_name の不足
Cloudflare経由のみ失敗SSLモード(Flexible/Full)とオリジンリダイレクト
全員に突然発生証明書期限切れまたはDNS変更

hstspreload.orgへの申請チェックリスト

申請は https://hstspreload.org/ のフォームから行う。以下をすべて満たしてから送信する。

インフラ・証明書

  • apex(example.com)が有効なHTTPS証明書で応答する
  • www.example.com が有効なHTTPS証明書で応答する
  • http://example.com301https:// へリダイレクトする
  • http://www.example.com301https:// へリダイレクトする
  • 証明書の自動更新(Let’s Encrypt / ACME 等)が稼働している

HSTSヘッダー

  • https://example.com のレスポンスに Strict-Transport-Security がある
  • max-age31536000 以上
  • includeSubDomains が含まれる
  • preload が含まれる
  • HTTP(平文)レスポンスにはHSTSヘッダーがない

サブドメイン

  • DNS上の全サブドメインを棚卸し済み
  • HTTPのみのサブドメインが残っていない
  • 証明書エラーのサブドメインが残っていない
  • 不要なワイルドカードDNSが第三者に悪用されない

申請・承認後

  • hstspreload.org のドメインチェッカーでエラーなし
  • 申請用メールアドレスで確認メールを受信・承認できる
  • 社内関係者に「Preload申請済み・取り消し困難」を共有した
  • 承認後、数週間〜数ヶ月待って chrome://net-internals/#hsts で登録を検証した

申請後のステータスは hstspreload.org から配信されるメール、または Chromium の hstspreload master list で確認できる。

Preload登録の取り消しが困難な理由

Preloadは「ブラウザに焼き込まれたHTTPS強制」に近い。通常のHSTSは max-age が切れればブラウザの記憶から消えるが、Preloadリストのドメインはブラウザをアップデートするまでリストに残り続ける。

解除に必要な作業(すべてを満たしても時間がかかる):

  1. HTTPSレスポンスから preload を削除し、includeSubDomains も外す
  2. max-age=0Strict-Transport-Security ヘッダーを配信し続ける(古いキャッシュの上書き用)
  3. hstspreload.org/removal/ から削除申請を行う
  4. 各ブラウザベンダーが次期リリースでリストを更新するまで待つ(数ヶ月〜1年以上の事例も報告されている)
  5. ユーザーのブラウザがそのバージョンに更新されるまで、古いリストが残る

つまり「一時的にHTTPに戻したい」「サブドメインだけHTTPにしたい」という要件が出た時点で、Preload済みドメインは選択肢が極端に少ない。新規ドメインへの移行や、検証環境の完全分離(別 registrable domain)を検討するケースも出る。

段階的導入のすすめ

いきなりPreload申請せず、(1) 短い max-age でHSTS投入 → (2) サブドメイン棚卸し → (3) max-age を1年へ延長 → (4) preload を付与して1週間観察 → (5) hstspreload.org 申請、の順が安全だ。

段階的導入のベストプラクティス

実務では次のフェーズで進めると、ロールバック余地を保ちながらPreloadまで到達できる。

フェーズ1 — リダイレクトと証明書(Preloadなし)

  • HTTP→HTTPSの301をapex/wwwに設定
  • 全サブドメインの証明書を確認
  • HSTSはまだ付けない、または max-age=0 で挙動確認

フェーズ2 — 短期HSTS(preloadなし)

add_header Strict-Transport-Security "max-age=300" always;

1週間、エラーログ・ユーザー問い合わせ・内部ツールの接続を監視する。

フェーズ3 — 本番HSTS(preloadなし)

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

includeSubDomains 付きで1ヶ月運用し、ステージングサブドメイン事故がないか確認する。

フェーズ4 — preload 付与と申請

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

1週間以上問題なければ hstspreload.org へ申請する。

運用面では、証明書の自動更新失敗アラート、HTTPリンクの残存チェック(メールテンプレート・OGP・旧ブックマーク)、CDNとオリジンの設定ドリフト検知をセットにすると、Preload後の事故を防ぎやすい。

関連記事

この記事は セキュリティ テーマの一環です。あわせて読むと理解が深まる関連記事をまとめました。

トピック記事
XSS攻撃と防御の完全ガイドXSS攻撃と防御の完全ガイド|反射型・蓄積型・DOM型の対策
CSRF対策の実装ガイドCSRF対策の実装ガイド|SameSite・Synchronizer Token・Laravel/Expressコード
Double Submit Cookie完全実装Double Submit Cookie完全実装|Synchronizer Token比較・SPA+JWT・サブドメイン攻撃
Content Security Policy (CSP) 設定ガイドContent Security Policy (CSP) 設定ガイド|XSS・クリックジャッキング対策
CSP Report-Only 段階導入ガイドCSP Report-Only 段階導入ガイド|本番を壊さずポリシーを検証する
セキュリティヘッダー一覧と設定ガイドセキュリティヘッダー一覧と設定ガイド|CSP・HSTS・Referrer-Policy・Permissions-Policy
セキュアなCookie設定ガイドセキュアなCookie設定ガイド|HttpOnly・Secure・SameSiteの使い分けとCSRF対策
CORSエラーの対処法CORSエラーの対処法|「Access-Control-Allow-Origin」で解決する
CORSプリフライトリクエストCORSプリフライトリクエスト|OPTIONSが飛ぶ条件と正しい応答
ローカル開発でCORSエラーが出る原因と3つの回避策ローカル開発でCORSエラーが出る原因と3つの回避策
OAuth2 PKCE SPA実装ガイドOAuth2 PKCE SPA実装ガイド|Authorization Code + S256 + BFFパターン
Passkeys・WebAuthn実装の基礎Passkeys・WebAuthn実装の基礎|FIDO2・Resident Key・@simplewebauthn/server

まとめ

HSTSは「HTTPSで一度訪れたユーザー」を守り、Preloadは「初めて訪れるユーザー」まで守る。nginx では443番の server ブロックに add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; を設定し、80番は301リダイレクトのみ。Cloudflare では Edge Certificates のHSTS設定で同等の値を有効化し、Flexible SSLを避ける。

申請前の必須作業は、全サブドメインのHTTPS化HTTPの301リダイレクトcurl と hstspreload.org による検証の3点。includeSubDomains は1つでもHTTPや証明書エラーが残ると致命的で、Preload登録後の取り消しは数ヶ月〜数年かかる。段階的導入でリスクを下げ、リスト登録は長期的なコミットメントとして計画してから hstspreload.org で申請しよう。