2026年8月16日

2026年8月16日

ApacheでHSTSを設定する方法|max-ageの段階的な上げ方とpreloadの注意点

HSTS(HTTP Strict Transport Security)は、「このサイトには今後 HTTPS だけで接続すること」をブラウザに覚えさせる仕組みです。Apache では mod_headers を使い、HTTPS の VirtualHost に Strict-Transport-Security ヘッダーを1行加えるだけで設定できます。ただし、いったんブラウザに記憶されると取り消しに時間がかかるため、短い max-age で試してから段階的に延ばす進め方が安全です。この記事では、設定の前提条件、VirtualHost と .htaccess それぞれの書き方、確認方法、includeSubDomains と preload を付ける前に確かめることを説明します。

HSTS で防げること・防げないこと

HTTP から HTTPS へのリダイレクトだけを設定している場合、訪問者が http:// の URL を開いた最初の1回は暗号化されていない通信が発生します。公衆無線LANなどでこの最初の通信を書き換えられると、HTTPS へ移る前に偽のページへ誘導される余地が残ります。

HSTS ヘッダーを受け取ったブラウザは、max-age で指定した期間、そのドメインへの http:// のアクセスを自動的に https:// に置き換えてから通信します。リダイレクトを待たずに HTTPS で接続するため、上のような途中の書き換えを防げます。また、HSTS が有効なサイトで証明書エラーが起きた場合、ブラウザは警告を無視して先へ進む操作を許可しません。

一方、HSTS は「初めて訪れるとき」の通信までは守れません。ヘッダーは HTTPS の応答で受け取って初めて記憶されるからです。この部分を補うのが後述の preload です。また、混在コンテンツ(外部ドメインの http:// の画像やスクリプト)を直す機能ではないため、HTTPS 化そのものは先に済ませておく必要があります。

設定する前に確認すること

HSTS は「もう HTTP には戻らない」という宣言です。次の点を先に確認します。

  • サイト全体が HTTPS で問題なく表示される。WordPress なら「設定」→「一般」の WordPress アドレスとサイトアドレスが https:// になっていて、混在コンテンツの警告が出ていない
  • HTTP へのアクセスが HTTPS へ 301 でリダイレクトされる
  • 証明書の自動更新が動いている。HSTS 有効後に証明書が切れると、訪問者は警告画面から先へ進めなくなる
  • includeSubDomains を付ける予定なら、すべてのサブドメイン(www、メール用の Web 画面、社内向けの管理画面、検証環境など)が HTTPS に対応している

設定ファイルを変更する前には、対象ファイルのバックアップを取っておきます。

mod_headers を有効にする

ヘッダーの追加には mod_headers を使います。Debian/Ubuntu 系では次のコマンドで有効にします。RHEL/AlmaLinux 系では標準で読み込まれていることが多く、httpd -M | grep headers で確認できます。

# Debian/Ubuntu系
sudo a2enmod headers
sudo apachectl -M | grep headers

headers_module と表示されれば使える状態です。

VirtualHost に設定する(推奨)

サーバーの設定ファイルを編集できる場合は、HTTPS 用の VirtualHost(<VirtualHost *:443>)に書きます。最初は max-age を 300秒(5分)程度にして、問題が起きてもすぐに元に戻せる状態で試します。

<VirtualHost *:443>
    ServerName example.com
    # ...証明書などの既存の設定...

    # 試験運用: 5分
    Header always set Strict-Transport-Security "max-age=300"
</VirtualHost>

always を付けるのは、リダイレクトやエラーの応答にもヘッダーを付けるためです。付けない場合、2xx 以外の応答にはヘッダーが付かないことがあります。

ブラウザは HTTP の応答に含まれる HSTS ヘッダーを無視する決まりになっているため、ポート80の VirtualHost には書く必要はありません。ポート80側は HTTPS へのリダイレクトだけを担当させます。

sudo apachectl configtest
sudo systemctl reload apache2   # RHEL系は httpd

Syntax OK を確認してから reload します。

.htaccess に設定する(共用サーバーの場合)

レンタルサーバーなどで設定ファイルを触れない場合は、ドキュメントルートの .htaccess に書きます。HTTPS で接続されたときだけヘッダーを付けるよう、<If> で条件を付けておくと意図がはっきりします。

<IfModule mod_headers.c>
    <If "%{HTTPS} == 'on'">
        Header always set Strict-Transport-Security "max-age=300"
    </If>
</IfModule>

WordPress の場合は # BEGIN WordPress から # END WordPress の外に書きます。この範囲はパーマリンク設定を保存したときに WordPress が書き換えるため、中に書いた行は消えることがあります。

CDN やロードバランサーで TLS を終端している構成では、Apache から見た接続は HTTP になり、%{HTTPS} が on になりません。この場合は CDN 側の HSTS 設定を使うか、前段から渡される X-Forwarded-Proto ヘッダーを条件にするなど、構成に合わせた方法を選びます。サーバー会社のコントロールパネルに HSTS の設定項目がある場合は、そちらを優先したほうが重複を避けられます。

設定されたかを確認する

curl でレスポンスヘッダーを確認します。

curl -sI https://example.com/ | grep -i strict-transport-security

strict-transport-security: max-age=300 のように1行だけ表示されれば反映されています。2行表示される場合は、セキュリティ系プラグインや CDN も同じヘッダーを付けています。Apache の Header always set は、PHP が出力したヘッダーを上書きしない場合があるため、どちらか一方に設定をまとめます。

Chrome では、アドレス欄に chrome://net-internals/#hsts を入力すると、ブラウザが記憶している HSTS の状態を照会したり、試験中のドメインの記憶を削除したりできます(画面の表記は Chrome のバージョンによって変わることがあります。2026年10月現在の情報です)。

max-age を段階的に延ばす

5分で数日運用して問題がなければ、値を延ばしていきます。目安は次のような順です。

# 1日
Header always set Strict-Transport-Security "max-age=86400"
# 1週間
Header always set Strict-Transport-Security "max-age=604800"
# 1年(本運用)
Header always set Strict-Transport-Security "max-age=31536000"

各段階で、HTTP でしか開けないページや、証明書の更新時の問題が出ないかを確かめます。延ばした後に問題が見つかっても、すでに訪れた人のブラウザは古い max-age の期間 HTTPS を強制し続けます。HSTS を止めたい場合は max-age=0 を送ると記憶が消えますが、これも訪問者が再度サイトを訪れて初めて効きます。

includeSubDomains と preload を付ける前に

includeSubDomains を付けると、example.com のすべてのサブドメインにも HTTPS が強制されます。HTTP でしか動かない社内システムや古い検証環境が1つでも残っていると、そこへアクセスできなくなります。サブドメインの一覧を DNS の設定から洗い出し、すべて HTTPS で開けることを確認してから付けます。

preload は、Chrome などのブラウザに最初から組み込まれている「HTTPS 専用ドメインの一覧」に登録を申請するための指定です。hstspreload.org の要件(2026年10月現在)は、おおむね次のとおりです。

  • 有効な証明書を使っている
  • HTTP を同じホストの HTTPS へリダイレクトしている
  • すべてのサブドメインが HTTPS に対応している
  • ルートドメインの HTTPS 応答で、max-age が1年(31536000秒)以上、includeSubDomains と preload を含むヘッダーを返している
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

一覧に登録されると、初めて訪れる人に対しても HTTPS が強制されます。その反面、一覧からの削除はブラウザの更新を待つ必要があり、反映まで長い時間がかかります。ヘッダーに preload を書いただけでは登録されず、サイトから申請して初めて一覧に載りますが、要件や手順は変わる可能性があるため、申請前に hstspreload.org の説明を読んでください。会社のドメインで使う場合は、管理者全員がサブドメインへの影響を理解したうえで判断することをおすすめします。

まとめ

  • HSTS は Apache の mod_headers で Strict-Transport-Security ヘッダーを HTTPS の応答に付けて設定する
  • 先に HTTPS 化・HTTP からの 301・証明書の自動更新を整えてから導入する
  • max-age は 300秒程度から始め、1日、1週間、1年と段階的に延ばす
  • 設定後は curl でヘッダーが1行だけ出ていることを確認し、プラグインや CDN との重複を避ける
  • includeSubDomains と preload は、すべてのサブドメインが HTTPS に対応していることを確かめてから付ける。preload の取り消しには長い時間がかかる

お気軽にご相談ください

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