Options MultiViewsのような拡張子省略をrewriteで実現する
Apache 2.2の頃から騙し騙し使っているWebシステムとか、いまだにhttpd.confや.htaccessに
Options +MultiViews
といった記述でPHPなどの拡張子を表に見せないようにしている環境ってけっこうあると思うんですよ。
はい、うちの環境の話ですw
例えば、実態は↓こんなURLでも
http://example.com/list.php/aaa/bbb/
↓こういうURLでもアクセスできるようにするって話。
http://example.com/list/aaa/bbb/
しかしそろそろApache2.4や、nginx等、新しい環境へ順次切り替えて行かなければならないため、Rewriteルールを使って今までのスクリプトでも動作するようにしよう、と試行錯誤しています。
list.phpはREQUEST_URIで処理分岐
以降の話はlist.phpの中で、下記のような処理分岐をしている前提で話を進めます。
if (isset($_SERVER['REQUEST_URI'])) {
$pathArray = explode('/', $_SERVER['REQUEST_URI']);
$mode = $pathArray[1];
} else {
$mode = '';
}
switch ($mode) {
case 'aaa';
require_once('aaa.html');
break;
case 'bbb':
require_once('bbb.html');
break;
default:
header("HTTP/1.0 404 Not Found");
exit();
}
Apache 2.4ではOptions MultiViewsで拡張子省略できないのでRewriteルールを使う
どうやらセキュリティ上の観点から、Options MultiViewsの仕様が変わったようです。
予期しない動作、例えば .old や .bak ファイルといったウェブマスタが送信を意図していない ファイルを送信する、といった動作を行なう可能性があります。
そのため、今後はOptions Multiviewsに頼らず、Rewriteルールで拡張子を省略するやり方へ移行したほうが良さそう。
よくあるRewriteルール
↓ググるとよく見かけるRewriteルール。
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php
リクエストされたURIのディレクトリが存在しなかった場合(上記2行目)、.phpを付けたファイルがあるかを確認し(上記3行目)、存在すれば.phpを付けて開く(上記4行目)。
つまり、↓これを
http://example.com/list.php
↓こう書けるようになる
http://example.com/list
……だけである。
このrewriteルールでは↓このリクエストでlist.phpを起動することは不可能。
http://example.com/list/aaa/bbb/
REQUEST_URIで分岐するPHP用のRewriteルール
もっと上手い方法があるのかも知れませんが、ぼくはこんなふうに書いています。
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} ^/list/.*$
RewriteRule .* /list.php
/list/ でアクセスしてきたら、ぜんぶ /list.php へ丸投げ。
処理分岐するphpファイルの数だけ必要ですが、ある程度まとめるためにREQUEST_URIで処理分岐するだろうし、なんだったら機能ごとにディレクトリに分けてその直下にあるindex.phpへ丸投げするのも良いと思います。
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.php
これを.htaccessに保存して、list1、list2、list3というサブディレクトリを作り、index.phpを配置すれば、
http://example.com/list1/aaa/bbb/
http://example.com/list2/aaa/bbb/
http://example.com/list3/aaa/bbb/
といったURLでアクセス可能になるでしょう。
同じrewriteルールをnginxで書く場合
nginxでは.htaccessは使えないため、nginx.confにルールを記述します。
location / {
try_files $uri $uri/ /index.php?$args;
}
nginxへWordPressをインストールする例でよく紹介される書き方ですが、WordPressはindex.phpですべてのURIを処理するのでこれで問題ありません。
ただ、先述したlist.phpのような自前のスクリプトで処理分岐するなら、↓こんな感じ。
location /list/ {
try_files $uri $uri/ /list.php?$args;
}
まず、locationの指定は基本的に前方一致です。そのため、/list/と記述しておけば、/list/aaa/だろうが/list/bbb/だろうが、その後のtry_filesが実行されます。
ちなみにlocationの後に ~ (チルダ)を付ければ正規表現も使えます。
try_filesの最初の2つ、$uriと$uri/については先述のRewriteルールの-fや-dと同じでリクエストされたURIのファイルもディレクトリも存在しない場合、という条件付けですね。
それらの条件に合致した場合、/list.phpに処理を任せるよ、という意味。
ちなみに、下記のような書き方でも一応動作しますが…
location / {
rewrite ^/list/.*$ /list.php;
}
これだと /list/image.jpg みたいに実際に存在するファイルへ直接アクセスできなくなるので注意が必要です。
listディレクトリなんて使わないぜ!という場合はtry_filesを使わず、rewrite一行でも良いでしょう。
まとめ
- Apache2.4以降ではOptions Multiviewsで拡張子の省略が出来ない。
- .htaccessへ記述するRewriteルールで拡張子の省略を実現してみた。
- ついでにnginxでもRewriteルールを記述してみた。
動作確認はしていますが、なにぶん勉強中のため勘違いや間違っているところがあるかも知れません。
その場合はご指摘頂くか、このおっさんおかしなこと書いてるぜ、と生暖かい目で見守って頂ければ幸いです。