2026年5月20日
2026年5月20日
WordPressのHSTSを設定する方法
はじめに
HSTS(HTTP Strict Transport Security)は、ブラウザに対して「このサイトは常にHTTPSで通信する」と宣言するセキュリティ機能です。一度HSTSを受信したブラウザは、以降HTTPでアクセスしようとしても自動的にHTTPSに切り替え、中間者攻撃を防ぎます。
症状・原因
- HTTPSは設定済みだが、HSTSが設定されていない
- セキュリティスキャンで「HSTS未設定」と指摘された
- ブラウザが毎回HTTPでアクセスしてから301リダイレクトしている
- SSL Labsのスコアを「A+」にしたい
解決手順
ステップ1:HSTSの設定前に確認すること
# ① HTTPS が完全に動作しているか確認(HSTSは後から設定)
curl -I https://example.com
# HTTP/2 200 が返ることを確認
# ② Mixed Content がないか確認
curl -s https://example.com/ | grep -oP 'src="http://[^"]+"|href="http://[^"]+"' | head -10
# HTTPリソースが残っていないことを確認
# ③ www / サブドメインも全てHTTPSか確認
curl -I https://www.example.com
curl -I https://subdomain.example.com
# ④ SSL証明書の有効期限を確認
certbot certificates | grep "Expiry Date"
ステップ2:Apacheで HSTSヘッダーを設定する
# /etc/apache2/sites-available/example.com-ssl.conf
<VirtualHost *:443>
# まずは短い max-age でテスト(5分 = 300秒)
Header always set Strict-Transport-Security "max-age=300"
# テスト後、問題なければ長期設定(1年 = 31536000秒)
# Header always set Strict-Transport-Security "max-age=31536000"
# サブドメインも含める場合(全サブドメインがHTTPS必須になる)
# Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# preloadリスト申請する場合(一度申請すると取り消し困難)
# Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</VirtualHost>
# mod_headers を有効化
sudo a2enmod headers
sudo systemctl reload apache2
# ヘッダーが設定されているか確認
curl -I https://example.com | grep -i strict
# Strict-Transport-Security: max-age=300
ステップ3:Nginxで HSTSヘッダーを設定する
# /etc/nginx/sites-available/example.com
server {
listen 443 ssl http2;
server_name example.com;
# まず短い max-age でテスト
add_header Strict-Transport-Security "max-age=300" always;
# 本番設定(1年)
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
sudo nginx -t && sudo systemctl reload nginx
curl -I https://example.com | grep -i strict
ステップ4:WordPressのfunctions.phpでHSTSを設定する
// functions.php: WordPress レベルでHSTSヘッダーを追加
// ※ Webサーバーレベルで設定できる場合はそちらが推奨
add_action('send_headers', function(): void {
if (!is_ssl()) return;
// ステージ1: テスト(max-age=300, 5分)
header('Strict-Transport-Security: max-age=300');
// ステージ2: 短期(max-age=86400, 1日)
// header('Strict-Transport-Security: max-age=86400');
// ステージ3: 本番(max-age=31536000, 1年)
// header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
});
// functions.php: セキュリティヘッダーをまとめて設定
add_action('send_headers', function(): void {
if (!is_ssl()) return;
// HSTS
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
// クリックジャッキング防止
header('X-Frame-Options: SAMEORIGIN');
// MIMEタイプスニッフィング防止
header('X-Content-Type-Options: nosniff');
// XSS対策(古いブラウザ向け)
header('X-XSS-Protection: 1; mode=block');
// リファラーポリシー
header('Referrer-Policy: strict-origin-when-cross-origin');
// Permissions Policy(カメラ・マイク等の制限)
header('Permissions-Policy: camera=(), microphone=(), geolocation=()');
});
ステップ5:HSTSプリロードリストへの登録
# HSTS プリロードリストの要件:
# 1. 有効なSSL証明書
# 2. HTTP → HTTPS の301リダイレクト
# 3. 全サブドメインがHTTPS
# 4. max-age が 31536000 以上
# 5. includeSubDomains が含まれる
# 6. preload ディレクティブが含まれる
# preload 対応ヘッダーを設定
Header always set Strict-Transport-Security \
"max-age=31536000; includeSubDomains; preload"
# 要件を確認
# https://hstspreload.org/ で example.com を入力してチェック
# 申請後の注意:
# ・削除には数ヶ月かかる
# ・全サブドメインが永続的にHTTPS必須になる
# ・慎重に検討してから申請する
注意事項
- HSTSを設定すると、
max-ageで指定した期間中、ブラウザはHTTPへの接続を拒否します。証明書に問題が発生した場合、訪問者はサイトにアクセスできなくなります。まず短いmax-age(300秒)でテストしてください includeSubDomainsを設定すると、全サブドメインもHTTPS必須になります。HTTPのみのサブドメインがある場合はアクセス不能になります- HSTSプリロードリストへの登録は「ブラウザのソースコードに焼き付ける」ようなものです。一度登録すると削除に数ヶ月〜1年かかります
まとめ
HSTS設定は①Mixed Content解消とHTTPS完全動作を確認してから実施、②Apache/Nginxの設定にStrict-Transport-Security: max-age=300を追加(短期テスト)、③問題なければmax-age=31536000に延長、④全サブドメインがHTTPS済みならincludeSubDomainsを追加、⑤WordPress管理画面ではsend_headersフックで他のセキュリティヘッダーとまとめて設定するのが効率的です。