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 が必要になると説明されています。

  1. リダイレクトや 404 など、Apache 自身が生成する 2xx 以外の応答にヘッダーを付けたいとき。always を付けないと、これらの応答にはヘッダーが付きません。
  2. 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

反映されない原因は、起きやすい順に次のとおりです。

  1. ページキャッシュや CDN が古い応答を返している: キャッシュを削除するか、キャッシュを通らない URL で確かめます
  2. .htaccess が読まれていない、または AllowOverride で許可されていない: 500 エラーやヘッダー無しの原因になります
  3. always の有無が合っていない: 404 やリダイレクト、PHP-FPM の応答で確認します
  4. 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 の順に原因を絞り込む

お気軽にご相談ください

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