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フックで他のセキュリティヘッダーとまとめて設定するのが効率的です。

お気軽にご相談ください

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