2026年5月27日
2026年5月27日
HTTPセキュリティヘッダーの設定ガイド(WordPressでの推奨値・設定場所・確認方法)
WordPress サイトで HTTP セキュリティヘッダーを設定するなら、壊れにくいヘッダー(X-Content-Type-Options、Referrer-Policy、Permissions-Policy、X-Frame-Options)から入れ、サイトの表示に影響しやすい Content-Security-Policy と Strict-Transport-Security は段階的に強めていく順番が安全です。この記事では、ヘッダーごとの意味と推奨値、Nginx・Apache・PHP のどこで設定するかの判断、設定が効いていないときの確認方法をまとめます。
セキュリティヘッダーは何を守るのか
セキュリティヘッダーは、サーバーがレスポンスに付けて「このページをブラウザでどう扱ってほしいか」を指示するものです。クリックジャッキング(他サイトの枠内に読み込んで誤操作させる攻撃)や、ファイル種別の誤判定、外部サイトへの不要な情報送信といったリスクを、ブラウザ側の制限で減らします。
一方で、ヘッダーは脆弱なプラグインやサーバーの設定不備そのものを直すものではありません。更新の適用やログイン保護と並ぶ「多層防御の一層」と考えるのが適切です。
まず入れたい基本のヘッダー
次の4つは、多くの WordPress サイトで表示を壊さずに導入できます。
X-Content-Type-Options
X-Content-Type-Options: nosniff
ブラウザが Content-Type を無視して中身からファイル種別を推測する動作(MIME スニッフィング)を止めます。アップロードされたファイルがスクリプトとして解釈されるリスクを減らせます。値は nosniff だけです。
X-Frame-Options と frame-ancestors
X-Frame-Options: SAMEORIGIN
自サイト以外のページが、自サイトを iframe で埋め込むことを禁止します。より新しい仕組みとして CSP の frame-ancestors ディレクティブがあり、両方を設定する場合は値をそろえておきます。なお WordPress コアは、管理画面とログイン画面に X-Frame-Options を送っています。サーバー側でも同じヘッダーを付けると重複することがあるので、後述の確認方法で見ておきます。
Referrer-Policy
Referrer-Policy: strict-origin-when-cross-origin
外部サイトへ移動するとき、URL のパスやクエリを送らずにドメインだけを送る設定です。現在の主要ブラウザは指定がなくてもこの値で動作しますが、明示しておくと意図がはっきりします。アクセス解析で外部からの参照元を細かく見ている場合も、この値なら自サイト内の遷移は従来どおり記録されます。
Permissions-Policy
Permissions-Policy: camera=(), microphone=(), geolocation=()
カメラ、マイク、位置情報などのブラウザ機能を、ページや埋め込みコンテンツが使えないようにします。地図やオンライン会議の埋め込みなど、実際に使う機能がある場合はその機能を外すか許可先を指定します。
X-XSS-Protection は入れなくてよい
古い記事では X-XSS-Protection: 1; mode=block が推奨されていることがありますが、この機能は主要ブラウザから削除されており、古いブラウザでは逆に問題を起こすことが知られています。設定しないか、明示的に 0 を指定します。
段階的に導入するヘッダー
Strict-Transport-Security(HSTS)
Strict-Transport-Security: max-age=300
ブラウザに「このドメインには今後 HTTPS でしか接続しない」と記憶させます。いったん長い期間を設定すると、HTTPS に問題が起きてもブラウザ側で HTTP に戻せなくなるため、最初は数分程度の短い max-age で動作を確認し、問題がなければ1年(31536000)へ延ばします。includeSubDomains を付けるのは、メールの管理画面や古い社内システムを含むすべてのサブドメインが HTTPS に対応していると確認できてからにします。HSTS プリロードリストへの登録はさらに取り消しに時間がかかるため、別途慎重に判断してください。
Content-Security-Policy(CSP)
CSP は、ページが読み込めるスクリプト・スタイル・画像などの出どころを制限し、XSS の被害を抑える強力なヘッダーです。ただし WordPress はテーマやプラグインがインラインのスクリプトやスタイルを多く出力するため、いきなり厳しい値で適用すると表示や管理画面が壊れます。まずは違反を報告するだけの Content-Security-Policy-Report-Only で始めます。
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; object-src 'none'
ブラウザの開発者ツールのコンソールに違反が表示されるので、実際に必要な読み込み先を確認しながら許可リストを調整します。上の例は Google タグマネージャーを使う場合を想定した出発点で、'unsafe-inline' が残っている間は XSS への防御効果が限られます。インラインスクリプトを nonce やハッシュで許可する方法へ移行できると効果が高まりますが、テーマやプラグインの対応状況に左右されるため、まずはフロントエンドから少しずつ進めるのが現実的です。
どこで設定するか:サーバーか PHP か
設定場所は大きく3つあり、どこで付けたかによって効く範囲が変わります。
Nginx で設定する
server {
# ...
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Strict-Transport-Security "max-age=300" always;
}
always を付けると、404 や 500 のようなエラー応答にもヘッダーが付きます。よくあるつまずきは、location ブロックの中で別の add_header を1つでも書くと、その location では server ブロックの add_header が引き継がれなくなる点です。静的ファイル用の location でキャッシュヘッダーを追加したら、そのファイルだけセキュリティヘッダーが消えた、というケースはこれが原因です。共通のヘッダーを別ファイルにまとめて include すると管理しやすくなります。設定変更後は sudo nginx -t で構文を確認してから再読み込みします。
Apache(.htaccess)で設定する
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>
mod_headers が有効である必要があります。.htaccess に書く場合は、WordPress が書き換える # BEGIN WordPress から # END WordPress の範囲の外に置きます。範囲内に書くと、パーマリンク設定の保存などで消えてしまいます。編集前には .htaccess のコピーを取っておきます。
PHP(mu-plugin)で設定する
サーバー設定を変更できない環境では、WordPress の send_headers アクションで付けられます。テーマの functions.php に書くとテーマ変更で消えるため、wp-content/mu-plugins/ に置くのがおすすめです。
<?php
/**
* Plugin Name: Security Headers
*/
add_action( 'send_headers', function () {
header( 'X-Content-Type-Options: nosniff' );
header( 'Referrer-Policy: strict-origin-when-cross-origin' );
header( 'Permissions-Policy: camera=(), microphone=(), geolocation=()' );
} );
PHP で付けたヘッダーは、WordPress が生成するページにしか付きません。画像・CSS・JavaScript などの静的ファイルや、ページキャッシュプラグインが静的な HTML として配信するページには付かないことがある点に注意してください。CDN を使っている場合は、CDN 側でレスポンスヘッダーを追加・上書きする機能もあります。
設定できているかを確認する
設定後は、実際のレスポンスを見て確認します。
curl -sI https://example.com/ | grep -iE "strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy"
curl -sI https://example.com/wp-content/themes/テーマ名/style.css
トップページ、投稿ページ、静的ファイル、存在しない URL(404)の4種類を確認すると、設定漏れを見つけやすくなります。同じヘッダーが2回出ている場合は、サーバーと PHP(またはプラグイン)の両方で付けていないかを確認します。ブラウザの開発者ツールの「ネットワーク」タブでも同じ内容を確認できます。
外部の診断サービスとしては、MDN の HTTP Observatory などがあります。ただし診断サービスの評価は目安であり、評価を上げるために必要な機能を止めてしまっては本末転倒です。各ツールの評価基準や提供元は変わることがあるため(2026年10月現在)、結果は参考情報として扱ってください。
不具合が出たときの切り分け
- 埋め込みが表示されない:X-Frame-Options や frame-ancestors で、自サイトを外部に埋め込む用途(他サイトへのウィジェット提供など)を止めていないか確認します
- 管理画面やエディターが動かない:CSP を管理画面にも適用していないか確認し、まずはフロントエンドだけに限定します
- 決済やソーシャルログインのポップアップが閉じない:Cross-Origin-Opener-Policy などを追加している場合、外部サービスとの連携に影響することがあるため外して確認します
- HTTP でアクセスできなくなった:HSTS がブラウザに記憶されています。サーバー側の設定を戻しても、
max-ageの期間はブラウザ側に残る点に注意します
まとめ
- 基本の4ヘッダー(X-Content-Type-Options、X-Frame-Options、Referrer-Policy、Permissions-Policy)から入れ、X-XSS-Protection は使いません
- HSTS は短い max-age で試してから延ばし、CSP は Report-Only で違反を確認しながら調整します
- Nginx の add_header の引き継ぎ、.htaccess の WordPress 管理範囲、PHP では静的ファイルに付かない点がよくあるつまずきです
- curl でページ・静的ファイル・404 のレスポンスを確認し、重複や設定漏れがないかを見ます