2026年6月3日

2026年6月3日

mod_rewriteのRewriteCondとRewriteRuleの書き方(構文・後方参照・フラグと実例)

mod_rewrite は、Apache が受け取った URL を条件に応じて書き換えたり、別の URL へリダイレクトしたりするモジュールです。中心になるのは2つのディレクティブで、RewriteRule が「どの URL をどう書き換えるか」、RewriteCond が「その書き換えをどんな条件のときに行うか」を決めます。

この記事では、構文と動きの規則を押さえたうえで、よく使う書き方を実例で説明します。例はすべて Apache 2.4(2026年11月時点の httpd 2.4 公式イメージ)で curl を使って結果を確かめています。

使う前の準備

Debian/Ubuntu では mod_rewrite を有効にして再起動します。

sudo a2enmod rewrite
sudo systemctl restart apache2

.htaccess に書く場合は、そのディレクトリで .htaccess の設定が許可されている必要があります。サーバー設定の <Directory> に次のような指定があるか確認してください。AllowOverride に FileInfo(または All)が含まれていないと .htaccess の Rewrite 系ディレクティブは使えず、Options に FollowSymLinks か SymLinksIfOwnerMatch が無いとリクエストは 403 になります。

<Directory /var/www/html>
    Options FollowSymLinks
    AllowOverride FileInfo
    Require all granted
</Directory>

どちらの場所に書く場合も、最初に RewriteEngine On が必要です。

RewriteRule の構文

RewriteRule パターン 置換先 [フラグ]
  • パターン: URL のパス部分に対する正規表現(PCRE)です。ホスト名やクエリ文字列(? 以降)は含まれません。
  • 置換先: 書き換え後のパス、または https:// から始まる URL です。- を書くと書き換えずにフラグだけを適用します。
  • フラグ: [R=301,L] のように角かっこの中にカンマ区切りで書きます。

.htaccess とサーバー設定でパターンが変わる

つまずきやすいのがここです。<VirtualHost> などのサーバー設定に書いた場合、パターンが照合する文字列は /oldpage.html のように先頭のスラッシュから始まります。一方、.htaccess(と <Directory> の中)では、そのディレクトリまでのパスが取り除かれ、oldpage.html のように先頭のスラッシュが付きません。

# サーバー設定(VirtualHost)に書く場合
RewriteRule ^/oldpage\.html$ /newpage.html [R=301,L]

# .htaccess に書く場合
RewriteRule ^oldpage\.html$ /newpage.html [R=301,L]

.htaccess に ^/oldpage\.html$ と書くと一致せず、何も起きません(実際に試すと 404 のままでした)。サーバー設定から .htaccess へ、またはその逆へ移すときは、パターンの先頭を見直してください。

RewriteCond の構文

RewriteCond テスト文字列 条件パターン [フラグ]

RewriteCond は、直後の1つの RewriteRule にだけかかります。複数の RewriteCond を並べると、すべてが成り立ったとき(AND)にだけ、そのすぐ下の RewriteRule が実行されます。2つ先のルールには効かないので、条件が必要なルールごとに書き直します。

テスト文字列には、%{変数名} の形でサーバー変数を使います。よく使うものは次のとおりです。

変数 中身の例
%{HTTP_HOST} リクエストのホスト名(example.com)
%{HTTPS} HTTPS なら on、そうでなければ off
%{REQUEST_URI} パス(/blog/index.php)。クエリ文字列は含まない
%{QUERY_STRING} ? 以降(id=15&cat=2)
%{REQUEST_FILENAME} 対応するファイルシステム上のパス
%{REMOTE_ADDR} 接続元の IP アドレス
%{HTTP:ヘッダー名} 任意のリクエストヘッダー(%{HTTP:X-Forwarded-Proto} など)

条件パターンには正規表現のほか、次の特別な指定が使えます。先頭に ! を付けると否定になります。

指定 意味
-f 通常のファイルとして存在する
-d ディレクトリとして存在する
-s サイズが 0 より大きいファイルとして存在する
-l シンボリックリンクとして存在する

RewriteCond のフラグは主に2つです。[NC] は大文字小文字を区別しない、[OR] は次の条件との関係を OR にします。

後方参照: $1 と %1

正規表現のかっこで取り出した部分は、置換先で使えます。

  • $1〜$9: RewriteRule のパターンで取り出した部分
  • %1〜%9: 最後に一致した RewriteCond の条件パターンで取り出した部分
# /blog/2024/05/hello-world → /articles/hello-world
RewriteRule ^blog/(\d{4})/(\d{2})/(.+)$ /articles/$3 [R=301,L]

よく使うフラグ

フラグ 意味
L このルールで処理を止める(後述の注意あり)
R=301 / R=302 外部リダイレクトする。R だけなら 302
NC 大文字小文字を区別しない
QSA 元のクエリ文字列を、置換先のクエリ文字列に追加する
QSD 元のクエリ文字列を捨てる(Apache 2.4 以降)
F 403 Forbidden を返す
G 410 Gone を返す
END 書き換えをここで終え、.htaccess の再処理も行わない(Apache 2.4 以降)
NE 置換先の # や % などをエスケープしない

.htaccess では、L で止まったあとも、書き換えた URL でもう一度 .htaccess の処理が最初から行われます。書き換え先が再び同じルールに一致すると、ループや意図しない二重の書き換えが起きます。書き換えをそこで終わらせたい場合は END を使います。

実例

次の例は、.htaccess にこの順で並べて動作を確かめたものです。リダイレクトのルールを先に、内部の書き換え(フロントコントローラー)を最後に置くのが基本です。

RewriteEngine On

# 1) example.com を www.example.com にそろえる
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

# 2) HTTP を HTTPS にリダイレクトする
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

# 3) 旧ページを新ページへ転送する
RewriteRule ^oldpage\.html$ https://www.example.com/newpage.html [R=301,L]

# 4) クエリ文字列の値を使って転送し、元のクエリは捨てる
RewriteCond %{QUERY_STRING} (?:^|&)id=([0-9]+)(?:&|$)
RewriteRule ^product\.php$ https://www.example.com/products/%1/ [R=301,L,QSD]

# 5) 実在しないパスをすべて index.php で処理する
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]

確かめた結果は次のとおりです。

リクエスト 結果
http://example.com/foo/bar?x=1 301 → https://www.example.com/foo/bar?x=1
http://www.example.com/foo 301 → https://www.example.com/foo
/oldpage.html(HTTPS) 301 → https://www.example.com/newpage.html
/product.php?cat=2&id=15 301 → https://www.example.com/products/15/(クエリなし)
/product.php?cat=2 書き換えなし(id が無いので条件に一致しない)
/some/pretty/url 200(index.php が応答)
/style.css(実在するファイル) 200(ファイルをそのまま返す)

それぞれのポイントです。

  • 1) RewriteRule のパターンにはクエリ文字列が含まれませんが、置換先にクエリを書いていない場合、元のクエリ文字列は自動で引き継がれます(?x=1 が残っています)。
  • 2) ロードバランサーや CDN で HTTPS を終端している場合、Apache から見ると %{HTTPS} は常に off です。X-Forwarded-Proto の条件を加えないと、リダイレクトが無限に繰り返されます。プロキシを使っていなければ、2行目の条件は不要です。
  • 3) 置換先を /newpage.html のようなパスだけにすると、リダイレクト先の http/https は Apache が受けた接続に合わせて決まります。HTTPS をプロキシで終端している環境では http:// へ転送されてしまうことがあるため、リダイレクトでは https:// から始まる URL を書いておくと迷いません。
  • 4) クエリ文字列は RewriteRule のパターンでは照合できないので、%{QUERY_STRING} を RewriteCond で調べ、取り出した値を %1 で使います。QSD が無いと、元の ?cat=2&id=15 が転送先に付いたままになります。
  • 5) WordPress などの CMS と同じ形です。実在するファイルやディレクトリは除外し、それ以外を index.php に渡します。

条件を OR でつなぐ例

管理画面と特定のパスへのアクセスを、決まった IP アドレス以外は 403 にする例です。最初の2つの条件は [OR] でつながり、その結果と3つ目の条件が AND になります。

RewriteCond %{REQUEST_URI} ^/admin [OR]
RewriteCond %{REQUEST_URI} ^/secret
RewriteCond %{REMOTE_ADDR} !^192\.0\.2\.
RewriteRule ^ - [F]

%{REQUEST_URI} は .htaccess でも先頭のスラッシュを含むので、この例はサーバー設定と .htaccess のどちらでも同じように書けます。

mod_rewrite を使わなくてよい場面

単純な転送だけなら、mod_alias の Redirect や RedirectMatch のほうが読みやすく、間違いも起きにくくなります。Apache の公式ドキュメントも、ほかの方法で済む場合は mod_rewrite を使わないよう勧めています。

Redirect 301 /oldpage.html https://www.example.com/newpage.html
RedirectMatch 301 ^/blog/(\d{4})/(\d{2})/(.+)$ https://www.example.com/articles/$3

条件(ホスト名、クエリ文字列、ファイルの有無など)で分けたいときが mod_rewrite の出番です。

動かないときの確かめ方

リダイレクトは curl で確かめる

ブラウザは 301 を記憶するため、ルールを直しても古い転送先へ飛び続けることがあります。curl -I https://www.example.com/oldpage.html で Location ヘッダーを見るか、試している間は R=302 にしておき、確定してから R=301 に変えます。

書き換えのログを出す

Apache 2.4 では RewriteLog は廃止され、LogLevel で出力します。サーバー設定(.htaccess ではなく VirtualHost など)に書き、エラーログを見ます。

LogLevel warn rewrite:trace3
sudo tail -f /var/log/apache2/error.log | grep rewrite

ログには、照合した文字列(.htaccess では先頭のスラッシュが取れたパス)、各条件の結果、書き換え後の値が出ます。trace レベルのログは量が多く処理も重くなるので、確認が終わったら元に戻してください。詳しい読み方はApacheのmod_rewriteをデバッグログで調べる方法でも説明しています。

設定の構文を確かめる

サーバー設定を変えたときは sudo apache2ctl configtest で確認してから再読み込みします。.htaccess の誤りは、アクセスしたときに 500 エラーとしてエラーログに出ます。

まとめ

  • RewriteRule パターン 置換先 [フラグ] で書き換え、RewriteCond で直後の1つのルールに条件を付けます
  • パターンはパスだけに照合し、.htaccess では先頭のスラッシュが付きません
  • $1 はルールのパターン、%1 は直前に一致した条件から取り出した値です
  • クエリ文字列は %{QUERY_STRING} で調べ、不要なら QSD で捨てます
  • リダイレクトを先に、フロントコントローラーのような内部の書き換えを最後に置きます
  • 単純な転送は Redirect で足り、確かめるときは curl と LogLevel rewrite:trace3 を使います

お気軽にご相談ください

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