LoginSignup
15
17

More than 5 years have passed since last update.

Alias したディレクトリで RewriteBase を使わずに rewrite する

Posted at

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/zzzaaa/zzz
  • 3. RewriteRule の条件のチェック
    • ^.*$ なのでマッチする
  • 4. パスの書き換え
    • aaa/zzzindex.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/zzzaaa/zzz
  • 3. RewriteRule の条件のチェック
    • ^.*$ なのでマッチする
  • 4. パスの書き換え
    • aaa/zzzindex.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 でも似たような方法が使われていたので、わたしが知らなかっただけで割りと有名な方法なのかも。

15
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
17