Apache mod_rewrite徹底ガイド:リダイレクト・内部リライト・確認方法【保存版】

apache
Apache mod_rewrite を「用語の意味 → 基本構文 → 実例(判定と実行を明示) → 動作確認 → 運用の注意」の順にまとめました。各サンプルは判定(RewriteCond)と実行(RewriteRule)を行コメントで示し、%1(条件のキャプチャ参照)、$1(ルールのキャプチャ参照)、HSTSの意味まで解説します。

まず押さえる用語ミニ辞典

  • RewriteEngine On:mod_rewriteを有効化。
  • RewriteCond TEST PATTERN [FLAG]判定を書く行。TEST(例:%{HTTP_HOST}, %{HTTPS}, %{REQUEST_URI})がPATTERNにマッチしたら。先頭に!否定=!=による文字列比較も可(例:RewriteCond %{HTTPS} =on)。
  • RewriteRule REGEX TARGET [FLAG]実行を書く行。リクエストパス(.htaccessならそのディレクトリ基準)がREGEXにマッチしたらTARGETへ書き換え/リダイレクト。
  • キャプチャと参照
    • (…)で囲んだ部分は「キャプチャ」。
    • RewriteRuleの正規表現のキャプチャは$1, $2…で参照。
    • RewriteCondでの直近のマッチは%1, %2…で参照(条件側のキャプチャ参照)。
  • 主なフラグ[L]=そこで評価打ち切り / [R=301]=恒久リダイレクト / [QSA]=既存クエリ保持 / [QSD]=既存クエリ破棄 / [NC]=大文字小文字無視 / [NE]=二重エスケープ防止 / [E=K:V]=環境変数設定 / [F]=403 / [G]=410。
  • HSTS(HTTP Strict Transport Security):ブラウザに「このドメインは常にHTTPSで開け」と命令するヘッダ(例:Strict-Transport-Security: max-age=31536000; includeSubDomains; preload)。先に全ページをHTTPSで安定運用してから有効化する(誤設定で戻せなくなるため要注意)。
  • .htaccessとVHostの違い:.htaccessはそのディレクトリ基準で^が解釈される。可能ならVHost(サーバ設定)側で完結させる方が速くて安全。

基本構文(.htaccess想定)

<IfModule mod_rewrite.c>
  RewriteEngine On

  # 判定(必要に応じて複数並べる)
  # RewriteCond <TEST> <PATTERN> [FLAGS]

  # 実行(マッチしたら置換/リダイレクト)
  # RewriteRule <REGEX> <TARGET> [FLAGS]
</IfModule>

実例(行コメント入り)+ていねい解説

1) HTTP→HTTPSへ統一(301)+HSTSの考え方

RewriteEngine On
# 判定:HTTPSでないなら(=on に等しくない)
RewriteCond %{HTTPS} !=on
# 実行:同じホスト・同じパスへ https で 301
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

解説:判定はRewriteCond %{HTTPS} !=on%{HTTPS}はTLSで来た時on。偽ならRewriteRuleが発動し、%{HTTP_HOST}(ホスト名)と%{REQUEST_URI}(パス&クエリ)をそのままHTTPSに乗せ換える。
HSTSは別レイヤ(ヘッダ)で有効化:Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"。ただし完全HTTPS運用が安定してから

2) wwwあり/なしの統一(301)

RewriteEngine On
# 判定:ホストが www. で始まる(^www\.(.+)$)
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
# 実行:%1 は 直前の RewriteCond の (.+) キャプチャ
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]

解説:^www\.(.+)$にマッチすると(.+)www.以降をキャプチャし、%1で参照(条件側のキャプチャ)。これで「非wwwのホスト名+同じパス」へ301。
逆(wwwなし→wwwあり)に統一するなら、判定を否定にする:

RewriteEngine On
# 判定:ホストが www. ではない
RewriteCond %{HTTP_HOST} !^www\. [NC]
# 実行:www. を付与
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

3) 末尾スラッシュの統一(301)

RewriteEngine On
# 判定:/で終わるパス
RewriteCond %{REQUEST_URI} .+/$
# 実行:スラッシュを外した同じパスへ
RewriteRule ^(.+?)/$ /$1 [R=301,L]

解説:判定は%{REQUEST_URI}…/で終わるか。実行は$1ルール側のキャプチャ)でパス本体を再利用。APIや特定ディレクトリを除外したい場合は、その判定(RewriteCond)を先に追加して早期除外。

4) フロントコントローラ(MVCやSPA)

RewriteEngine On
# 判定:物理ファイル/ディレクトリが存在しない
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# 実行:index.php に内部振り分け
RewriteRule ^ index.php [L]

解説:!-f/!-dで「無い時だけ」流す。静的ファイル(画像/CSS/JS)は素通りで高速。

5) 旧URL→新URL(301)

RewriteEngine On
# 判定:/old-path または /old-path/
RewriteRule ^old-path/?$ /new-path [R=301,L]

解説:この例は判定をRewriteRuleの正規表現に直書き(シンプルな一致)。大量移行はRewriteMapで外部表を引くと速くて管理もしやすい。

6) クエリを保持/破棄(QSA/QSD)

# 既存クエリを保持し page=1 を追加
RewriteRule ^search$ /search.php?page=1 [QSA,L]

# 既存クエリを破棄して正規URLへ
RewriteRule ^temp$ /article [QSD,R=301,L]

解説:[QSA]はQuery String Append(保持+追加)、[QSD]はDiscard(破棄)。トラッキングパラメータ排除や重複URLの抑制に有効。

RewriteEngine On
# 判定:Referer が空でない かつ 自ドメイン以外
RewriteCond %{HTTP_REFERER} !^$ 
RewriteCond %{HTTP_REFERER} !example\.com [NC]
# 実行:画像拡張子なら403
RewriteRule \.(png|jpe?g|gif|webp)$ - [F,L]

解説:HTTP_REFERERは偽装可能なので抑止目的。別画像へ誘導したい場合は[R=302]でプレースホルダーへ。

8) 拡張子隠し(/about → /about.php)

RewriteEngine On
# 判定:対応する .php が物理に存在する
RewriteCond %{REQUEST_FILENAME}\.php -f
# 実行:/about → /about.php に内部振替
RewriteRule ^(.+)$ $1.php [L]

解説:存在チェックしてから振る。Options -MultiViews(コンテンツネゴシエーションの無効化)推奨。MultiViewsが有効だと競合しやすい。

「本当に動いてる?」確認の実践

1) リダイレクトはヘッダだけ確認

curl -I http://example.com/old-path
# → HTTP/1.1 301 Moved Permanently
#    Location: https://example.com/new-path

コツ:-L(追従)を付けると最終URLしか見えない。原因追跡は付けない。

2) 内部リライトは「環境変数+一時ヘッダ」で可視化

# 実行時に環境変数をセット
RewriteRule ^ index.php [E=REWRITE_HIT:1,L]
# VHost/Directory 側でヘッダ出力(mod_headers)
Header set X-Rewrite "1" env=REWRITE_HIT

解説:内部リライトはブラウザから見えないので、ヒット時のみX-Rewrite: 1を返して観測。調査が終わったら削除。

3) 詳細ログ(期間限定)

# Apache 2.4
LogLevel rewrite:trace2   # 0~8程度。調査後は warn に戻す

4) アクセスログに印を付ける

# 例:ヒット時に環境変数 HIT=1 を付与
RewriteRule ^ - [E=HIT:1]
# ログフォーマットに %{HIT}e を埋め込む
LogFormat "%h %l %u %t \\"%r\\" %>s %b \\"%{HIT}e\\"" vhost
CustomLog ${APACHE_LOG_DIR}/access.log vhost

有効化・配置とチューニング

  • mod_rewrite有効化(Debian系):a2enmod rewritesystemctl reload apache2
  • .htaccessよりVHost推奨AllowOverride Noneが基本。やむなく使う時は必要最小限。
  • 評価コスト削減!-f/!-dや否定条件で早期除外。正規表現は^$でアンカー。
  • 大量301RewriteMap(テキスト/プログラム)で一括変換。
  • プロキシ/CDN併用:Cloudflare等の前段があると%{HTTPS}やIPの見え方が変わる。X-Forwarded-Proto等を考慮し、必要ならApache側で信頼プロキシ設定を行う。

ハマりやすいところ

  • リダイレクトチェーン:301→301…は避け、一発で最終URLへ。
  • 二重エンコード:URLに%が含まれる場合は[NE]で保護することがある。
  • MultiViewsの干渉Options -MultiViewsを検討。
  • HSTSのロックイン:HSTSは戻しにくい。HTTPS完全移行が安定してから。



このページの設定はベースラインです。トラフィックやアプリ、CDN/WAF 構成に合わせて調整してください。

コメント

タイトルとURLをコピーしました