読者です 読者をやめる 読者になる 読者になる

端っこプログラマーの手帳

主にプログラムに関する手記です

【PHP】VirtualHostにリライト設定を書いたときの SCRIPT_NAME の値がおかしい

PHPのサーバ環境変数に、SCRIPT_NAME という値があります。
「現在の実行ファイルのパス」が入り、例えば、以下のようなリライト設定で、 RewriteRuleに引っかかるリクエストでは、「/index.php」が入ります。

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]

しかし設定の記述場所によっては、「/index.php」が入らないケースがあるようです。
リライトの設定場所って...なんか毎回ハマっている気がします。

リライト設定を記述できる場所は2箇所。

  • Directoryディレクティブ(=.htaccess
  • VirtualHost内

Directoryディレクティブまたは、.htaccess の場合は、どちらに書いても「/index.php」になります。 (以下は、Directoryディレクティブの場合。)
しかし、VirtualHost内では、なんと「/index.php」とはなりません。

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html
    DirectoryIndex index.php index.html

    <Directory "/var/www/html">
        <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^(.*)$ index.php [QSA,L] 
        </IfModule>
    </Directory>
</VirtualHost>

VirtualHost内に書いた場合

httpd.conf の設定
<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html
    DirectoryIndex index.php index.html

    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ /index.php [QSA,L] 
    </IfModule>
</VirtualHost>
ファイル構成

ドキュメントルート直下に、index.php がおいてある(/var/www/html/index.php

リクエストと結果

上記内容で、http://example.com/hoge/hige/ にアクセスします。
すると、SCRIPT_NAME の値は「/hoge/hige/」なります。 これは、REQUEST_URI と同じ値で、「/index.php」を期待しますがそうはならないのです。

何が違うのか?

VirtualHostに書いた場合とDirectoryディレクティブに書いた場合とで動作はどのように違うのでしょうか。リライトログを追ってみました。

■ VirtualHost リライトログ(※メッセージ部分のみ抽出)

[example.com/sid#7f1ac4223418][rid#7f1ac4433670/initial] init rewrite engine with requested uri /hoge/hige/
[example.com/sid#7f1ac4223418][rid#7f1ac4433670/initial] applying pattern '^(.*)$' to uri '/hoge/hige/'
[example.com/sid#7f1ac4223418][rid#7f1ac4433670/initial] RewriteCond: input='/hoge/hige/' pattern='!-f' => matched
[example.com/sid#7f1ac4223418][rid#7f1ac4433670/initial] rewrite '/hoge/hige/' -> '/index.php'
[example.com/sid#7f1ac4223418][rid#7f1ac4433670/initial] local path result: /index.php
[example.com/sid#7f1ac4223418][rid#7f1ac4433670/initial] prefixed with document_root to /var/www/html/index.php
[example.com/sid#7f1ac4223418][rid#7f1ac4433670/initial] go-ahead with /var/www/html/index.php [OK]

■ Directoryディレクティブ リライトログ(※メッセージ部分のみ抽出)

[example.com/sid#7fa563ae9418][rid#7fa563cf9670/initial] [perdir /var/www/html/] add path info postfix: /var/www/html/hoge -> /var/www/html/hoge/hige/
[example.com/sid#7fa563ae9418][rid#7fa563cf9670/initial] [perdir /var/www/html/] strip per-dir prefix: /var/www/html/hoge/hige/ -> hoge/hige/
[example.com/sid#7fa563ae9418][rid#7fa563cf9670/initial] [perdir /var/www/html/] applying pattern '^(.*)$' to uri 'hoge/hige/'
[example.com/sid#7fa563ae9418][rid#7fa563cf9670/initial] [perdir /var/www/html/] RewriteCond: input='/var/www/html/hoge' pattern='!-f' => matched
[example.com/sid#7fa563ae9418][rid#7fa563cf9670/initial] [perdir /var/www/html/] rewrite 'hoge/hige/' -> 'index.php'
[example.com/sid#7fa563ae9418][rid#7fa563cf9670/initial] [perdir /var/www/html/] add per-dir prefix: index.php -> /var/www/html/index.php
[example.com/sid#7fa563ae9418][rid#7fa563cf9670/initial] [perdir /var/www/html/] strip document_root prefix: /var/www/html/index.php -> /index.php
[example.com/sid#7fa563ae9418][rid#7fa563cf9670/initial] [perdir /var/www/html/] internal redirect with /index.php [INTERNAL REDIRECT]
[example.com/sid#7fa563ae9418][rid#7fa563ce44f0/initial/redir#1] [perdir /var/www/html/] strip per-dir prefix: /var/www/html/index.php -> index.php
[example.com/sid#7fa563ae9418][rid#7fa563ce44f0/initial/redir#1] [perdir /var/www/html/] applying pattern '^(.*)$' to uri 'index.php'
[example.com/sid#7fa563ae9418][rid#7fa563ce44f0/initial/redir#1] [perdir /var/www/html/] RewriteCond: input='/var/www/html/index.php' pattern='!-f' => not-matched
[example.com/sid#7fa563ae9418][rid#7fa563ce44f0/initial/redir#1] [perdir /var/www/html/] pass through /var/www/html/index.php

詳しく見ると、Directoryディレクティブ では、内部リダイレクトが発生して、2回リライト設定を通っていることが分かります。 リダイレクト前と後では、リクエストパスが変わります。

1回目 … /hoge/hige/
2回目 … /index.php

一方で、VirtualHostに書いた場合は、リクエストは繰り返されることはありません。1回のみです。

1回目 … /hoge/hige/ ★

どうやら、★で記したところの情報が、PHPの$_SERVER['SCRIPT_NAME']に入るようです。(これは上の結果からの推測ですが...)
この値は、Apache上では、REQUEST_FILENAME という変数になるため、SCRIPT_NAMEは、 内部リダイレクトした時も含めた最終リクエスト時の REQUEST_FILENAME が入る といったところではないでしょうか。

ただ、内部リダイレクトあるなしの挙動の違いだけで、これ以上詳しいことは分かりませんでした。 Directoryディレクトティブで内部リダイレクトなしにできないのかとか 逆に、VirtualHostで内部リダイレクトできないのか等が気になります。

リライト設定はDirectoryディレクティブへ

VirtualHost設定に書いた場合は、罠が多いように思います。
.htaccess と記述を合わせることのできるメリットもありますし、リライト設定はDirectoryディレクティブへ書くのが好ましいと思いました。