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が使えなくなると、その期間はサイトを開けません。次の順で延ばすのが安全です。

  1. max-age=300(5分)で設定し、サイトと管理画面、サブドメインに問題がないか確認する
  2. 問題がなければ max-age=86400(1日)、max-age=2592000(30日)と延ばす
  3. 最終的に 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 から始め、問題がないことを確かめてから延ばす

関連記事:

お気軽にご相談ください

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