2026年5月29日

2026年5月29日

WordPressのHTTPセキュリティヘッダーを強化して診断スコアを上げる手順

セキュリティヘッダーの診断サイトで低い評価が出たときは、X-Content-Type-Options、Referrer-Policy、Permissions-Policy、X-Frame-Options(または CSP の frame-ancestors)、Strict-Transport-Security、Content-Security-Policy を順に追加していくと、多くの場合は評価が上がります。ただし、スコアはヘッダーの有無を見るチェックリストであり、サイトの安全性そのものではありません。この記事では、各ヘッダーの意味と安全な初期値、Apache・Nginx・WordPress(PHP)それぞれでの設定方法、サイトを壊さずに CSP を導入する進め方を説明します。

診断ツールのスコアとの付き合い方

ヘッダーの診断には、securityheaders.com や MDN HTTP Observatory などがよく使われます。どちらもURLを入れるだけで、送られているヘッダーの一覧と不足しているものを示してくれます。採点基準や運営元は変わることがあるため、スコアの数字より「何が足りないと言われているか」を見るのがおすすめです(2026年10月現在)。

注意したいのは、スコアを上げるために厳しすぎる設定を入れると、画像が出ない、埋め込み動画が再生されない、ブロックエディターが動かないといった不具合が起きることです。特に CSP と HSTS は影響が大きいので、後半で説明する段階的な進め方をとってください。

追加するヘッダーと初期値

比較的安全に入れられるもの

  • X-Content-Type-Options: nosniff — ブラウザがファイルの種類を推測して別の形式として実行するのを防ぎます。副作用はほとんどありません。
  • Referrer-Policy: strict-origin-when-cross-origin — 外部サイトへ移動するときに、URLのパスやクエリを渡さずドメインだけを送ります。現在の主要ブラウザの既定値と同じ考え方で、明示しておくと診断で評価されます。
  • Permissions-Policy — カメラ、マイク、位置情報などのブラウザ機能を、使わないなら無効にすると宣言します。例: camera=(), microphone=(), geolocation=()。地図やフォームでこれらの機能を使っているなら、その機能は外してください。
  • X-Frame-Options: SAMEORIGIN — 他サイトの iframe に自サイトを埋め込ませないようにし、クリックジャッキングを防ぎます。WordPressはログイン画面と管理画面ではこのヘッダーを自動で送っています。公開ページにも付けたい場合に追加します。新しい仕組みとしては CSP の frame-ancestors 'self' が同じ役割を持ちます。

古い診断で求められることがあるもの

X-XSS-Protection は、かつてブラウザに搭載されていたXSSフィルター向けのヘッダーです。現在の主要ブラウザはこの機能を廃止しており、MDNなどでも送らないか 0 にすることが推奨されています。古い診断ツールで不足と表示されても、対策として追加する意味はほとんどありません。

慎重に入れるもの

  • Strict-Transport-Security(HSTS) — 一度受け取ったブラウザは、指定期間そのドメインにHTTPSでしか接続しなくなります。HTTPSの設定に問題が出ると、期間が切れるまで閲覧できなくなるため、最初は max-age=300(5分)など短い値で始め、問題がないことを確認してから延ばします。includeSubDomains を付ける場合は、すべてのサブドメインがHTTPSに対応しているかを先に確認してください。ブラウザに最初からHTTPSを強制させるプリロードリストへの登録には、max-age が1年以上、includeSubDomains と preload の指定などが条件になり、登録後の取り消しには時間がかかります。スコアのためだけに申請するのは避けてください。
  • Content-Security-Policy(CSP) — 読み込んでよいスクリプトや画像の出どころを指定し、XSSの被害を抑えます。もっとも効果が大きい反面、もっとも壊れやすいヘッダーです。

どこで設定するか

ヘッダーは、Webサーバー、WordPress(PHP)、CDNのいずれかで付けられます。基本はWebサーバーでの設定がおすすめです。PHPで付ける方法では、WordPressが生成したページにしかヘッダーが付かず、画像やCSSなどの静的ファイルや、ページキャッシュがPHPを通さずに返すHTMLには付かないことがあるからです。

複数の場所で同じヘッダーを設定すると、重複して送られ、ブラウザが意図しない解釈をすることがあります。どこで設定するかを1か所に決めてください。

Apache(.htaccess)の例

mod_headers が有効なサーバーで、.htaccess の WordPress のブロック(# BEGIN WordPress)の外に追記します。編集前にファイルのバックアップを取ってください。

<IfModule mod_headers.c>
    Header always set X-Content-Type-Options "nosniff"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
    Header always set X-Frame-Options "SAMEORIGIN"
    # 最初は短い期間で試す。問題がなければ段階的に延ばす
    Header always set Strict-Transport-Security "max-age=300"
</IfModule>

HSTSはHTTPSの応答に付いたものだけがブラウザに有効と扱われ、HTTPの応答に付いていても無視されます。

Nginx の例

HTTPSの server ブロック内に書きます。

add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Strict-Transport-Security "max-age=300" always;

Nginxの add_header には、location ブロックの中で別の add_header を書くと、外側(server ブロック)の指定が引き継がれなくなるという性質があります。静的ファイル用の location でキャッシュ用のヘッダーを追加している場合は、そこにもセキュリティヘッダーを書くか、共通部分を include でまとめてください。設定を変えたら nginx -t で構文を確認してから再読み込みします。

WordPress(mu-plugin)で付ける例

サーバーの設定を変更できない場合は、wp-content/mu-plugins/ にファイルを置いて send_headers アクションで付けます。テーマの更新で消えないよう、テーマの functions.php ではなく mu-plugin にしておくと管理しやすくなります。

<?php
/**
 * Plugin Name: Security Headers
 * Description: 公開ページにセキュリティヘッダーを付ける
 */

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=()' );
	header( 'X-Frame-Options: SAMEORIGIN' );
} );

send_headers は、WordPressが公開ページを生成するときに実行されるアクションです。管理画面には適用されません。

CSP をサイトを壊さずに導入する

CSP は、いきなり本番で有効にせず、Content-Security-Policy-Report-Only ヘッダーで「違反を報告するだけ」の状態から始めます。このヘッダーではブラウザは読み込みを止めず、違反をデベロッパーツールのコンソールに表示します。

Header always set Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data: https:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'; base-uri 'self'; object-src 'none'"

この状態で主要なページを開き、コンソールに出る違反を確認します。アクセス解析、広告、埋め込み動画、Webフォントなど、外部から読み込んでいるものがあれば、そのドメインを該当する項目に追加します。

WordPressのテーマやプラグインはインラインスクリプトを多く使うため、最初は 'unsafe-inline' を許可せざるを得ないことがよくあります。これは CSP の効果を弱める設定ですが、frame-ancestors、base-uri、object-src の制限だけでも一定の効果があります。インラインスクリプトに nonce を付けて 'unsafe-inline' を外す方法もありますが、すべてのプラグインが対応しているわけではないので、段階的に進める前提で取り組んでください。

違反が出なくなったことを確認してから、ヘッダー名を Content-Security-Policy に変えて有効にします。管理画面やブロックエディターは CSP の影響を受けやすいため、まずは公開ページだけに適用し、管理画面への適用は別に検証するのが安全です。

設定後の確認

設定したら、実際に返っているヘッダーをコマンドで確認します。

curl -sI https://example.com/ | grep -iE 'strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy'

同じヘッダーが2回出ていれば、複数の場所で設定しています。画像やCSSのURLでも同じコマンドを実行し、静的ファイルにも付いているかを確認します。そのあとに診断ツールで再チェックし、加えてサイトの主要な機能(フォーム送信、決済、埋め込み、ログイン、ブロックエディター)が問題なく動くかを確かめてください。CDNを使っている場合は、CDN側でヘッダーが変更・追加されていないかも確認します。

まとめ

  • 診断スコアはヘッダーの有無のチェックであり、スコアより「何が足りないか」を見る
  • X-Content-Type-Options、Referrer-Policy、Permissions-Policy、X-Frame-Options は比較的安全に追加できる
  • HSTS は短い max-age から始め、サブドメインのHTTPS対応を確認してから延ばす
  • CSP は Report-Only で違反を確認してから有効にし、まず公開ページから適用する
  • 設定場所は1か所に決め、curl -I で重複や静的ファイルへの適用を確認する

お気軽にご相談ください

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