hogehoge foobar Blog Style Beta

Web,Mac,Linux,JavaScript,Perl,PHP,RegExp,Git,Vim,Redmineなど技術的なことのメモや、ちょっと便利そうなものの紹介をしています。

クエリ文字列(URLパラメタ)があるURLでのmod_rewriteの使い方

mod_rewriteを使用してクエリ文字列(URLパラメタ)があるURLを変換してリダイレクトしようとしたときに少しハマリかけたので、備忘録として書いておきます。

今回リダイレクトしたURL

今回は、以下の旧URLから新URLにリダイレクトさせるケースでやってみました。
内容としては、旧URLのクエリ文字列(URLパラメタ)の「id」「name」に設定された値が、新URLではディレクトリとファイル名として使用できるようにするといったものです。

旧URL 新URL
形式 /foo/index.html?id=数字列&name=文字列 /foo/文字列/数字列.html
サンプル /foo/index.html?id=123&name=bar /foo/bar/123.html

RewriteRuleで試す

まずは「RewriteRule」で正規表現を使ってリダイレクトする設定を書いてみました。
URLの書き換えは正規表現で行ないます。
「恒久的なサイトの移転」とするために「R=301」を指定してます。

RewriteEngine On
RewriteRule ^foo/index.html\?id=(\d{3})&name=([a-z]+)$ /foo/$2/$1.html? [R=301,L]

上記の設定をした上で、ページにアクセスしてみます。
ページへのアクセスは普通にブラウザからでも可能ですが、今回は「telnet」でアクセスしてみます。
「telnet」でアクセスはコマンドライン上で実行出来るのと、HTTPステータスコード等が明確に確認できるのでなかなか便利です。

$ telnet localhost 80
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /foo/index.html?id=123&name=bar HTTP/1.0
User-Agent: Telnet [ja] (Linux)
Host: localhost

HTTP/1.1 404 Not Found
Date: Thu, 21 Oct 2010 23:31:15 GMT
Server: Apache
Accept-Ranges: bytes
Connection: close
Content-Type: text/html; charset=none

「GET /foo/index.html?id=123&name=bar」でアクセスしましたが、リダイレクトされずに404で戻ってきてしまっています。

RedirectMatchで試す

次にRewriteRule(mod_rewrite)ではなく、RedirectMatchを使ってみました。
正規表現の書き方とかはほとんど同じです。
「恒久的なサイトの移転」とするために「permanent」を指定してます。これは「301」と指定しても同じ意味になります。

RedirectMatch permanent ^/foo/index.html?id=(\d{3})&name=([a-z]+)$ /foo/$2/$1.html

先程と同じようにtelnetでhttpアクセスをしてみます。

$ telnet localhost 80
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /foo/index.html?id=123&name=bar HTTP/1.0
User-Agent: Telnet [ja] (Linux)
Host: localhost

HTTP/1.1 404 Not Found
Date: Thu, 21 Oct 2010 23:34:53 GMT
Server: Apache
Accept-Ranges: bytes
Connection: close
Content-Type: text/html; charset=none

状況は変わらず、リダイレクトされずに404で戻ってきてしまっています。

RewriteRuleはクエリ文字列(URLパラメタ)を含んでいない

手元にあった「Apacheクックブック第2版」で調べてみるとこのような記述がありました。

mod_rewriteのマッチングと書き換え処理では、クエリ文字列(URLパラメタ)をURIの一部として見なさないため、別々に扱う必要がある。
%{QUERY_STRING}を参照する必要がある。
RewriteRuleではクエリ文字列(URLパラメタ)無しのURIしか見ない。

Apacheクックブック第2版 p94より抜粋

つまりは、「RewriteRule」で書き換えられる側のURIには「クエリ文字列(URLパラメタ)が含まれない」ため、上記の方法では正規表現がマッチせずにそのまま「/foo/index.html」を表示してしまっていたようです。
※「RedirectMatch」についての記述はありませんでしたが、WEBで調べてみると同様の問題のようでした。

RewriteCondとRewriteRuleを使う

ということで、「RewriteRule」以外に「RewriteCond」でクエリ文字列(URLパラメタ)を抽出する条件を追加してみます。
クエリ文字列(URLパラメタ)は「%{QUERY_STRING}」に格納されています。
今回の例の場合だと「%{QUERY_STRING}」には「id=123&name=bar」という文字列がセットされていることになります。

これを踏まえた上で設定してみます。

Options +FollowSymLinks
RewriteEngine On
RewriteCond %{QUERY_STRING} id=([0-9]{3})&name=([a-z]+)$
RewriteRule ^foo/index.html$ /foo/%2/%1.html? [R=301,L]

またまた同じようにtelnetでhttpアクセスをしてみます。

$ telnet localhost 80
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /foo/index.html?id=123&name=bar HTTP/1.0
User-Agent: Telnet [ja] (Linux)
Host: localhost

HTTP/1.1 301 Moved Permanently
Date: Thu, 21 Oct 2010 23:36:44 GMT
Server: Apache
Location: http://localhost/foo/bar/123.html
Content-Length: 246
Connection: close
Content-Type: text/html; charset=iso-8859-1

HTTPヘッダ部分の「HTTP/1.1 301 Moved Permanently」と「Location: http://localhost/foo/bar/123.html」から、今度はhttp://localhost/foo/bar/123.htmlへリダイレクトされていることが確認できました。
ブラウザからアクセスしても正常にアクセスすることができました。

ちょっとハマリかけましたが、mod_rewriteっておもしろいです。

今回参考にしたページ

Apache module mod_rewrite
http://japache.infoscience.co.jp/japanese_1_3_6/manual/mod/mod_rewrite.html

URIのリダイレクト設定をやってみた(管理人日記) - むぅもぉ.jp
http://muumoo.jp/news/2006/04/06/0redirect.html

クエリー付のリダイレクトは「mod_rewrite」を使うべし Project MultiBurst
http://www.multiburst.net/project-multiburst/archives/2007/01/07/1923.php

telnetでブラウズ(HTTP)
http://ash.jp/net/telnet_http.htm