こんにちは、一年前に買ったデニムのサイズが合わなくなってきた島田です。
皆さん食べ過ぎには注意しましょう。。。
(俺が悪いんじゃない、会社周辺に美味しいお店が多いからいけないんだ!!)
では、早速本題。
CakePHP環境下で静的コンテンツは、通常『インストールフォルダ/app/webroot』の配下に設置しますよね。
/app/webroot/test/index.html
にファイルを設置すれば、
example.com/test/index.html
example.com/test/
といった感じのURLでアクセスできます。
ではこの状態で、 example.com/test (『/』なし)にアクセスするとどうでしょう?
どん!
そうなんです。
『/app/webroot/』 が付いた状態にリダイレクトされちゃうんです。
※ mod_rewite をとおさず直接アクセスしてますねぇ。
画像やCSSファイルへのパスは、相対的位置関係が変わらないので、表示が崩れるということはありませんが、なんだか気持ち悪い。。。
同じ情報のページに2URLからアクセス出来るのはSEO的に良くないし、
Google Analytics などでアクセスログを取得しているときは、同一コンテンツなのに集計結果がわかれてしまう。。。
ということで、こいつを解決しちゃいます。
まずは Apache のログを見てみよう
問題の解決に移る前にまず Apache のログを見てみましょう。
192.168.0.1 - - [19/Dec/2010:23:08:06 +0900] "GET /test HTTP/1.1" 301 354 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"
192.168.0.1 - - [19/Dec/2010:23:08:07 +0900] "GET /app/webroot/test/ HTTP/1.1" 200 928 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"
301エラーを返したあとで、『/app/webroot/test/』にリダイレクトしています。
簡単にいうとこんな感じです。
- スラッシュなしでアクセスしたため、Apache がファイルとして『test』を検索
- ファイルは見当たらなかったがディレクトリは見つかったので、クライアントに301エラーとリダイレクトメッセージ(『/』つきのURL)を返す
- リダイレクトメッセージにあるURLに再度リクエストを投げる
- コンテンツを表示
2. で返ってくる『リダイレクトメッセージ』を直してリダイレクト先を変えてやればいいのか思いきや、
それだと直接『example.com/app/webroot/test/』にアクセスされてしまう。。。
というわけで、今回は
に飛んできたURLを
にリダイレクトさせるという方法で問題を解決しちゃいます。
.htaccess を改変しよう
CakePHP ではデフォルトで 3つの .htaccess が設置されていますが、今回は『インストールフォルダ/app/webroot/.htaccess』を改変します。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>
インストール時のままだとこんな感じです。
これをこんな感じに改変します。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
RewriteCond %{THE_REQUEST} "^(.+?) (.*?)/app/webroot/(.*?) (.+?)$"
RewriteRule ^(.*?)$ %2/%3 [R=301,L]
</IfModule>
THE_REQUEST とは 先程のログの 『”GET /app/webroot/test/ HTTP/1.1″』 の部分を返します。
要するにホストネーム以下のURLに /app/webroot/ が含まれていたら、それを省いたものへリダイレクトすると書かれています。
Apache のログはこんな感じ
192.168.0.1 - - [19/Dec/2010:23:18:06 +0900] "GET /test HTTP/1.1" 301 354 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"
192.168.0.1 - - [19/Dec/2010:23:18:06 +0900] "GET /app/webroot/test/ HTTP/1.1" 301 342 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"
192.168.0.1 - - [19/Dec/2010:23:18:07 +0900] "GET /test/ HTTP/1.1" 200 928 "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; ja-jp) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4"
二回リダイレクトして目的のURLにたどりついているのがわかります。
最後に
今回の解法は、Rewriteの条件を追加していますので、どうしてもその部分にコストが追加でかかってしまいます。
レスポンススピードをシビアに求められるコンテンツには向かない場合もありますので、注意してください。
それでは、また次回お会いしましょう。
さようなら。
追記
RewriteRule でURLを書き換えた時、自動的に URLエンコード が行われてしまいます。
クエリ文字列に特殊文字を含む場合、意図しないURLに書き変わってしまいますので、 NEフラグを指定してエンコードを行わないようにした方がよいでしょう。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
RewriteCond %{THE_REQUEST} "^(.+?) (.*?)/app/webroot/(.*?) (.+?)$"
RewriteRule ^(.*?)$ %2/%3 [R=301,L,NE]
</IfModule>