2026年9月18日
2026年9月18日
Apacheのmod_headersでHTTPヘッダーを操作する方法(追加・上書き・削除と注意点)
Apache で HTTP ヘッダーを追加・上書き・削除するには mod_headers を有効にし、レスポンスには Header、バックエンドへ渡すリクエストには RequestHeader を使います。この記事では基本の書き方、つまずきやすい always の意味、セキュリティヘッダーやキャッシュ制御など WordPress サイトでよく使う例、反映されないときの確認方法を順に解説します。なお、モジュールの正式名は mod_header ではなく「mod_headers」です。
手順1:mod_headersを有効にする
Debian / Ubuntu 系では次のコマンドで有効にします。RHEL / AlmaLinux / Rocky Linux 系の httpd パッケージでは、標準で読み込まれていることがほとんどです。
# Debian / Ubuntu
sudo a2enmod headers
sudo apachectl configtest
sudo systemctl reload apache2
# 読み込み確認(RHEL 系は httpd -M)
apache2ctl -M | grep headers
設定はバーチャルホストの設定ファイルか .htaccess に書きます。.htaccess で Header を使うには、サーバー側で AllowOverride FileInfo(または All)が許可されている必要があります。モジュールが無い環境で 500 エラーにならないよう、.htaccess では <IfModule mod_headers.c> で囲むのが一般的です。
手順2:Headerディレクティブの基本を押さえる
書式は Header [条件] 動作 ヘッダー名 [値] [early|env=...|expr=...] です。よく使う動作は次のとおりです。
set: ヘッダーを設定する。同名のヘッダーがあれば置き換えるappend: 既存の値にカンマ区切りで追加するmerge: 同じ値がまだ無いときだけ追加する(重複を避けたいとき向き)add: 同名のヘッダーをもう1行追加する(Set-Cookie 以外では重複の原因になりやすい)unset: ヘッダーを削除するsetifempty: そのヘッダーが無いときだけ設定するedit/edit*: 正規表現で値を書き換える
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header merge Vary "Accept-Encoding"
Header unset ETag
</IfModule>
条件を付けたい場合は expr= が使えます。たとえば HTTPS のときだけ付ける、特定のパスだけに付ける、といった指定ができます。
Header always set X-Example "on" "expr=%{HTTPS} == 'on'"
「always」を付けるかどうかが最大のつまずきどころ
mod_headers には内部的に2つのヘッダー表があり、条件を省略した Header set(onsuccess)と Header always set は別の表を操作します。公式ドキュメントでは、次の2つの場面で always が必要になると説明されています。
- リダイレクトや 404 など、Apache 自身が生成する 2xx 以外の応答にヘッダーを付けたいとき。
alwaysを付けないと、これらの応答にはヘッダーが付きません。 - CGI や mod_proxy_fcgi(PHP-FPM)が出力したヘッダーを変更・削除したいとき。これらのヘッダーは
always側の表に入るため、Header unsetだけでは消えません。
WordPress を PHP-FPM で動かしている構成で「PHP が付けたヘッダーを消したいのに残る」「同じヘッダーが2行出る」という症状の多くは、ここが原因です。どちらの構成でも効かせたい削除は、両方を書いておくと安心です。
<IfModule mod_headers.c>
# mod_php 構成と PHP-FPM 構成の両方で X-Powered-By を消す
Header unset X-Powered-By
Header always unset X-Powered-By
</IfModule>
なお X-Powered-By は、php.ini の expose_php = Off で PHP 側から出さないようにするのが本来の対処です。また Server ヘッダーは mod_headers では削除できません。表示内容を減らすには ServerTokens Prod を使います。
よく使う設定例1:セキュリティヘッダー
多くのサイトで使われる基本的なセキュリティヘッダーの例です。エラーページやリダイレクトにも付くよう always を付けています。
<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 X-Frame-Options "SAMEORIGIN"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>
X-Frame-Options は古くからあるヘッダーで、新しいブラウザでは Content-Security-Policy の frame-ancestors が同じ役割を担います。CSP は WordPress の管理画面やブロックエディターがインラインスクリプトを多用するため、いきなり強制すると管理画面が動かなくなることがあります。導入するなら Report-Only で影響を確かめてからにしてください。HSTS(Strict-Transport-Security)は一度設定すると取り消しに時間がかかるため、別記事の手順に沿って短い期間から試すことをすすめます。
よく使う設定例2:静的ファイルのキャッシュ制御
CSS・JS・フォント・画像に長めの Cache-Control を付けると、再訪問時の読み込みが速くなります。
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|woff2|svg|png|jpe?g|gif|webp|avif)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
</IfModule>
WordPress はテーマやプラグインの CSS・JS に ?ver= を付けて出力するため、更新時は URL が変わり、長いキャッシュでも新しいファイルが読み込まれます。注意が必要なのは、メディアライブラリで同じファイル名の画像を差し替えた場合や、子テーマの CSS をバージョン固定で読み込んでいる場合です。この場合は immutable を外して期間を短くするか、ファイル名やバージョンを変える運用にしてください。HTML(PHP の出力)にはこの設定をかけないことも大切です。mod_expires を併用している場合は、両方で Cache-Control を出していないかも確認します。
よく使う設定例3:RequestHeaderでバックエンドに情報を渡す
Apache をリバースプロキシとして使い、後ろのアプリケーションサーバーに WordPress がある構成では、RequestHeader で元の通信が HTTPS だったことを伝えます。
<VirtualHost *:443>
ServerName example.com
RequestHeader set X-Forwarded-Proto "https"
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
WordPress は標準ではこのヘッダーを見ないため、wp-config.php で $_SERVER['HTTP_X_FORWARDED_PROTO'] が https のときに $_SERVER['HTTPS'] = 'on'; とする処理を加えるのが一般的です。これを忘れると、管理画面でリダイレクトが繰り返されることがあります。外部から偽の X-Forwarded-Proto を送られても困らないよう、プロキシ側で set(上書き)しておくのがポイントです。
WordPress側で付ける方法との使い分け
ヘッダーは PHP からも付けられます。WordPress には send_headers アクションと wp_headers フィルターがあり、ログイン状態や投稿の種類など WordPress の情報に応じて変えたいヘッダーは、こちらのほうが書きやすくなります。一方、静的ファイルにも付けたいヘッダーや、サイト全体に一律で付けるヘッダーは Apache 側が向いています。
両方で同じヘッダーを付けると重複します。セキュリティ系プラグインがヘッダーを出力していることもあるため、Apache に設定を足す前に現在の応答を確認してください。
反映されないときの確認方法
確認には curl が便利です。-I は HEAD リクエストのため、GET と応答が異なるサイトでは -D - で GET のヘッダーを表示します。
curl -sI https://example.com/ | grep -iE 'x-content-type|referrer|cache-control|x-powered-by'
curl -s -D - -o /dev/null https://example.com/wp-content/themes/your-theme/style.css
反映されない原因は、起きやすい順に次のとおりです。
- ページキャッシュや CDN が古い応答を返している: キャッシュを削除するか、キャッシュを通らない URL で確かめます
- .htaccess が読まれていない、または
AllowOverrideで許可されていない: 500 エラーやヘッダー無しの原因になります alwaysの有無が合っていない: 404 やリダイレクト、PHP-FPM の応答で確認します- mod_headers が読み込まれていない:
<IfModule>で囲んでいると、エラーにならずに無視されます
設定を変える前は元のファイルをバックアップし、apachectl configtest を通してから再読み込みしてください。本記事は Apache 2.4 系(2026年10月現在)を前提にしています。
まとめ
- モジュール名は mod_headers。レスポンスは
Header、バックエンドへのリクエストはRequestHeaderで操作する - 置き換えは
set、重複を避けた追加はmerge、削除はunset - エラー応答やリダイレクトに付けるとき、PHP-FPM が出したヘッダーを消すときは
alwaysが必要 Serverヘッダーは mod_headers では消せない。ServerTokens Prodを使う- 静的ファイルの長期キャッシュは
?ver=の仕組みと差し替え運用を踏まえて設定する - 確認は curl で行い、キャッシュ・AllowOverride・always の順に原因を絞り込む