Alias したディレクトリで rewrite が動作しない例
例えば、次のように Alias を設定します。
DocumentRoot /var/www/html
Alias /ore/sandbox /home/ore/sandbox/html
/home/ore/sandbox/html
には下記の .htaccess
を配置します。
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
そして http://example.com/ore/sandbox/aaa/zzz
のような URL を開いても、意図した通りにリライトされずに 404 Not Found になります。
Alias を使わずに動作するとき
上手く動作しない理由を説明するために、Alias を使わずに正しく動作する例を簡単に説明します。
なお RewriteCond
でファイルやらディレクトリやらの存在チェックをしている部分はあんまり関係ないので説明しません。
Alias を使わないので http://example.com/ore/sandbox/aaa/zzz
へのリクエストは /var/www/html/ore/sandbox/aaa/zzz
に解決されます。/var/www/html/ore/sandbox/
には↑の例と同じ内容の .htaccess
を配置しているものとします。
-
1.
mod_rewrite
には/var/www/html/ore/sandbox/aaa/zzz
というパスが渡される/var/www/html/ore/sandbox/aaa/zzz
-
2.
.htaccess
のディレクトリまでのパスを削除-
/var/www/html/ore/sandbox/aaa/zzz
→aaa/zzz
-
-
3.
RewriteRule
の条件のチェック-
^.*$
なのでマッチする
-
-
4. パスの書き換え
-
aaa/zzz
→index.php
-
-
5. 2. で除去したパスをくっつける
-
index.php
→/var/www/html/ore/sandbox/index.php
-
-
6. ドキュメントルートを削除
-
/var/www/html/ore/sandbox/index.php
→/ore/sandbox/index.php
-
-
7. 書き換えられたパスに内部リダイレクト
/ore/sandbox/index.php
Alias を使って動作しないとき
Alias を使った場合は http://example.com/ore/sandbox/aaa/zzz
へのリクエストが /home/ore/sandbox/html/aaa/zzz
に解決されます。
-
1.
mod_rewrite
には/home/ore/sandbox/html/aaa/zzz
というパスが渡される/home/ore/sandbox/html/aaa/zzz
-
2.
.htaccess
のディレクトリまでのパスを削除-
/home/ore/sandbox/html/aaa/zzz
→aaa/zzz
-
-
3.
RewriteRule
の条件のチェック-
^.*$
なのでマッチする
-
-
4. パスの書き換え
-
aaa/zzz
→index.php
-
-
5. 2. で除去したパスをくっつける
-
index.php
→/home/ore/sandbox/html/index.php
-
-
6. ドキュメントルートを削除
- と言いたいところだがパスのプレフィックスが違うので取り除けない
-
7. 書き換えられたパスに内部リダイレクト
/home/ore/sandbox/html/index.php
/home/ore/sandbox/html/index.php
などという URL は存在しないので 404 Not Found です。
ちなみに /home/ore/sandbox/html/index.php
で何か適当なものが表示されるようにしておけば、そいつが表示されます。
<Location /home/ore/sandbox/html/index.php>
SetHandler server-status
</Location>
これはキモい。
RewriteBase を指定したとき
Alias したディレクトリで mod_rewrite
を使うときは RewriteBase
を指定する必要があります。
RewriteEngine On
RewriteBase /ore/sandbox
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
すると、先ほどの「6. ドキュメントルートを削除」以降が次のように変わります。
-
6.
.htaccess
のディレクトリまでのパスを RewriteBase のパスに書き換え-
{/home/ore/sandbox/html/}index.php
→{/ore/sandbox/}index.php
-
-
7. 書き換えられたパスに内部リダイレクト
/ore/sandbox/index.php
RewriteBase を指定せずにどうにかする
zend-expressive-skeleton を眺めていたら、RewriteBase を使わずにどうにかしているっぽいのを見つけました。
# RewriteCond
RewriteCond %{REQUEST_URI}::$1 ^(/.+)(.+)::\2$
# RewriteRule(1)
RewriteRule ^(.*) - [E=BASE:%1]
# RewriteRule(2)
RewriteRule ^(.*)$ %{ENV:BASE}index.php [NC,L]
これは次のように動作をします。
- RewriteRule(1) で最初のサブパターンにパスをキャプチャ
$1 := aaa/zzz
- RewriteCond の左辺
-
%{REQUEST_URI}::$1
→/ore/sandbox/aaa/zzz::aaa/zzz
-
- RewriteCond の右辺に次のようにマッチ(イメージ)
(/ore/sandbox/)(aaa/zzz)::aaa/zzz
- RewriteRule(1) が1個目のサブパターンを環境変数に設定
BASE := /ore/sandbox/
- RewriteRule(2) で
BASE
をくっつけたパスに書き換え-
%{ENV:BASE}index.php
→/ore/sandbox/index.php
-
- そのまま内部リダイレクト
/ore/sandbox/index.php
さいごに
symfony-standard でも似たような方法が使われていたので、わたしが知らなかっただけで割りと有名な方法なのかも。