2026年5月20日
2026年5月20日
WordPressのセキュリティヘッダー設定方法|.htaccess・Nginx・functions.php別
HTTPセキュリティヘッダーは、ブラウザーに「このページをどう扱うか」を伝えるレスポンスヘッダーです。クリックジャッキングやMIMEタイプの誤判定、リファラーからの情報漏れ、HTTPへのダウングレードを減らせます。WordPressでは、Apacheの .htaccess、Nginxの設定ファイル、functions.php のいずれかで設定します。この記事では、それぞれの正しい書き方と、設定してもヘッダーが出ない・サイトが500エラーになる原因を説明します。
設定する主なヘッダー
| ヘッダー | 役割 | 設定例 |
|---|---|---|
X-Frame-Options |
他サイトの iframe に埋め込まれるのを防ぐ(クリックジャッキング対策) | SAMEORIGIN |
X-Content-Type-Options |
ブラウザーがMIMEタイプを推測して別の種類として扱うのを防ぐ | nosniff |
Referrer-Policy |
他サイトへ移動するときに送るリファラーの範囲を決める | strict-origin-when-cross-origin |
Permissions-Policy |
カメラ・マイク・位置情報などのブラウザー機能の利用を制限する | camera=(), microphone=(), geolocation=() |
Strict-Transport-Security(HSTS) |
以後のアクセスをHTTPSに限定するようブラウザーに指示する | max-age=300から始めて延ばす |
古い記事でよく見かける X-XSS-Protection: 1; mode=block は設定しません。主要なブラウザーはこのヘッダーが対象とするXSSフィルターをすでに廃止しており、MDNなどでも設定しないこと(送る場合は 0)が推奨されています。XSSへの対策は、コンテンツセキュリティポリシー(CSP)で行います。
X-Frame-Options は DENY にすると、カスタマイザーのプレビューのように同じサイトを iframe で表示する機能が動かなくなります。WordPressの管理画面用には SAMEORIGIN を使ってください。なお、2026年10月時点のWordPressは、管理画面とログイン画面では自分で X-Frame-Options: SAMEORIGIN と Content-Security-Policy: frame-ancestors 'self' を送っています。
設定する前に:現在のヘッダーを確認する
まず、今どのヘッダーが返っているかを確認します。example.com は自分のドメインに置き換えます。
curl -sI https://example.com/ | grep -iE "x-frame|x-content|strict-transport|referrer|permissions|content-security|x-powered-by|^server"
ブラウザーの開発者ツールの「ネットワーク」タブで、ページのレスポンスヘッダーを見ても確認できます。
Apache(.htaccess)で設定する
WordPressのルートにある .htaccess の、# BEGIN WordPress より前に追記します。mod_headers が有効になっている必要があります(Debian/Ubuntuの自前サーバーなら sudo a2enmod headers で有効にします。レンタルサーバーでは多くの場合有効です)。
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
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=()"
# HSTS はHTTPSのときだけ送る。最初は max-age=300(5分)で試す
Header always set Strict-Transport-Security "max-age=300" "expr=%{HTTPS} == 'on'"
# PHP が付ける X-Powered-By を消す(根本は php.ini の expose_php = Off)
Header unset X-Powered-By
</IfModule>
.htaccess に書いてはいけない設定
サーバー情報を隠す目的で ServerTokens Prod を .htaccess に書く例がありますが、ServerTokens はサーバー全体の設定(httpd.conf など)でしか使えないディレクティブです。.htaccess に書くと、サイト全体が500 Internal Server Errorになります。自前のサーバーで設定する場合は、次のようにサーバーの設定ファイルに書いて再起動します。
# 例: Debian/Ubuntu の /etc/apache2/conf-available/security.conf
ServerTokens Prod
ServerSignature Off
レンタルサーバーでは、この設定は利用者側から変更できないことがほとんどです。
X-Powered-By はPHPが付けるヘッダーなので、php.ini で expose_php = Off にするのが根本的な対処です。レンタルサーバーでは管理画面のphp.ini設定で変更できる場合があります。
Nginxで設定する
Nginxでは add_header を使います。always を付けると、エラーページ(4xx・5xx)にもヘッダーが付きます。
server {
# ... 既存の設定 ...
add_header X-Frame-Options "SAMEORIGIN" always;
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 Strict-Transport-Security "max-age=300" always;
# バージョン番号を隠す(「nginx」という名前自体は残る)
server_tokens off;
location ~ \.php$ {
# ... 既存の fastcgi の設定 ...
# PHP-FPM が付ける X-Powered-By を消す(標準モジュールで使える)
fastcgi_hide_header X-Powered-By;
}
}
more_clear_headers でヘッダーを消す例もありますが、これは標準のNginxには含まれない headers-more モジュールのディレクティブです。モジュールを入れていない環境で書くと、nginx -t で unknown directive のエラーになり、設定を反映できません。PHPの X-Powered-By を消すだけなら、上の fastcgi_hide_header で足ります。
add_header が消える落とし穴
Nginxの add_header は、内側のブロック(location など)に1つでも add_header があると、外側の server ブロックに書いた add_header を引き継ぎません。たとえば画像のキャッシュ用に location の中で add_header Cache-Control ... を書いていると、その location のレスポンスにはセキュリティヘッダーが付きません。その場合は、セキュリティヘッダーを別ファイルにまとめ、各 location で include します。
# /etc/nginx/snippets/security-headers.conf にセキュリティヘッダーの add_header を並べておく
location ~* \.(?:jpg|jpeg|png|gif|webp|svg|css|js)$ {
add_header Cache-Control "public, max-age=2592000";
include snippets/security-headers.conf;
}
設定後は sudo nginx -t で構文を確認し、sudo systemctl reload nginx で反映します。
functions.php で設定する(サーバー設定を変えられない場合)
.htaccess も Nginx の設定も触れない場合は、WordPressの send_headers アクションでヘッダーを送れます。
<?php
// functions.php(または mu-plugin): PHP からセキュリティヘッダーを送る
add_action( 'send_headers', function () {
header( 'X-Frame-Options: SAMEORIGIN' );
header( 'X-Content-Type-Options: nosniff' );
header( 'Referrer-Policy: strict-origin-when-cross-origin' );
header( 'Permissions-Policy: camera=(), microphone=(), geolocation=()' );
if ( is_ssl() ) {
header( 'Strict-Transport-Security: max-age=300' );
}
header_remove( 'X-Powered-By' );
} );
// 単一の投稿で付く X-Pingback ヘッダーを外す
add_filter( 'wp_headers', function ( $headers ) {
unset( $headers['X-Pingback'] );
return $headers;
} );
この方法には次の制限があります。
send_headersはWordPressがフロントのページを表示するときに実行されます。管理画面、画像・CSS・JavaScriptなどWebサーバーが直接返す静的ファイルには付きません。ServerヘッダーはWebサーバーが付けるため、PHPのheader_remove()では消せません。- ページキャッシュのプラグインやCDNがHTMLを直接返す場合、キャッシュから返したページにはPHPで送ったヘッダーが付かないことがあります。
できるだけサーバー側(.htaccess か Nginx)で設定し、PHPと両方に書かないようにしてください。両方に書くと、同じヘッダーが2回送られることがあります。
HSTS は短い期間から始める
Strict-Transport-Security を受け取ったブラウザーは、max-age の秒数の間、そのサイトにHTTPSでしか接続しなくなります。証明書の期限切れなどでHTTPSが使えなくなると、その期間はサイトを開けません。次の順で延ばすのが安全です。
max-age=300(5分)で設定し、サイトと管理画面、サブドメインに問題がないか確認する- 問題がなければ
max-age=86400(1日)、max-age=2592000(30日)と延ばす - 最終的に
max-age=31536000(1年)にする
includeSubDomains を付けると、すべてのサブドメインにも同じ指示が適用されます。HTTPSに対応していないサブドメインがあるなら付けないでください。HSTSプリロードリストへの登録は、取り消しに時間がかかるため、運用が安定してから検討します。
Permissions-Policy は使っている機能を確認してから
camera=() のように空のかっこを指定すると、その機能をページ内のどこからも使えなくなります。サイトの機能(たとえば位置情報を使う独自のスクリプト)で使っている場合は、geolocation=(self) のように許可する範囲を指定します。設定後は、問い合わせフォームや埋め込みコンテンツなど、主要な機能が動くかを確認してください。
設定後の確認
curl -sI https://example.com/ | grep -iE "x-frame|x-content|strict-transport|referrer|permissions|x-powered-by"
次のように返っていれば設定できています(値は設定によって異なります)。
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
strict-transport-security: max-age=300
トップページだけでなく、投稿ページ、画像のURL、存在しないURL(404のページ)でも確認します。404で付いていなければ、Apacheなら always、Nginxなら always の付け忘れを、画像で付いていなければNginxの add_header の引き継ぎを疑ってください。
まとめ
- 基本の5つは
X-Frame-Options・X-Content-Type-Options・Referrer-Policy・Permissions-Policy・Strict-Transport-Security。X-XSS-Protectionは設定しない - .htaccess には
ServerTokensを書かない(500エラーになる)。サーバーの設定ファイルに書く - Nginxの
more_clear_headersは headers-more モジュールが必要。X-Powered-Byはfastcgi_hide_headerかexpose_php = Offで消す - Nginxの
add_headerは、内側のブロックにadd_headerがあると外側の設定を引き継がない - functions.php の
send_headersは、管理画面や静的ファイルには効かない - HSTS は
max-age=300から始め、問題がないことを確かめてから延ばす
関連記事: