QA@IT

【Apache 2.4】サーバー全体で共通のRewriteRuleとAliasを設定したい

7667 PV

先日【Apache 2.4】サーバ設定ファイルに記述したRewriteRuleが動かない - QA@ITで質問させていただきました。最終的にやりたいことは、そちらで質問させていただいたときと同じです。拡張子無しのURIでアクセスできるようにし、拡張子付きのURIを拡張子無しのURIにリダイレクトさせたいです。

CentOS 6.4 上で、Apache 2.4.6 を利用しています。

サーバ設定ファイル コンテキストにRewriteRuleディレクティブを記述せずに、「拡張子無しのURIでアクセス」を実現したいと思い、書き方を替えてみました。しかし、Aliasディレクティブが絡むリクエストの場合、内部リダイレクト後にエラーが起きてしまいます。

<VirtualHost *:80>
    ServerName default-virtual-host.invalid
    Redirect permanent / http://example.jp/
</VirtualHost>
<VirtualHost *:80>
    ServerName example.jp
    DocumentRoot /var/www/html
</VirtualHost>
<VirtualHost *:80>
    ServerName subdomain.example.jp
    DocumentRoot /var/www/subdomain
    AliasMatch ^/(index|style)$ /var/www/share/subdomain/core/$1
    <Directory /var/www/share/subdomain/core>
        Require all granted
    </Directory>
</VirtualHost>

RewriteEngine on
LogLevel alert rewrite:trace8

# /var/www/標準入力 をファイルシステムの絶対パスとして扱い、拡張子付きのファイルを探し、最初に見つかったファイルのパスから /var/www/ を取り除いて返す
RewriteMap adjust-extension "prg:/usr/bin/perl -e '$| = 1; while (<STDIN>) { chomp($_); @file = glob(\\'/var/www/\\' . $_ . \\'.*\\'); @file[0] =~ s#^/var/www/##; print((@file[0] || \\'NULL\\') . \"\\\\n\"); }'"

# 拡張子無しのURIでアクセスできるようにする
<Directory /var/www>
    RewriteRule ^([^/]+/[^.]+)$ ${adjust-extension:$1|$1}
</Directory>

Alias /polyfill /var/www/common/polyfill

このような設定ファイルを読み込んだ上で http://example.jp/index にアクセスすると、意図通り/var/www/html/index.xhtmlが返ります。しかし http://example.jp/polyfill/main にアクセスすると 404 Not Found が返ります。http://example.jp/polyfill/main.es にアクセスすると、意図通り/var/www/common/polyfill/main.esが返ります。

[rewrite:trace3] [initial] [perdir /var/www/] strip per-dir prefix: /var/www/common/polyfill/main -> common/polyfill/main
[rewrite:trace3] [initial] [perdir /var/www/] applying pattern '^([^/]+/[^.]+)$' to uri 'common/polyfill/main'
[rewrite:trace5] [initial] map lookup OK: map=adjust-extension key=common/polyfill/main -> val=common/polyfill/main.es
[rewrite:trace2] [initial] [perdir /var/www/] rewrite 'common/polyfill/main' -> 'common/polyfill/main.es'
[rewrite:trace3] [initial] [perdir /var/www/] add per-dir prefix: common/polyfill/main.es -> /var/www/common/polyfill/main.es
[rewrite:trace1] [initial] [perdir /var/www/] internal redirect with /var/www/common/polyfill/main.es [INTERNAL REDIRECT]
[rewrite:trace3] [initial/redir#1] [perdir /var/www/] add path info postfix: /var/www/html/var -> /var/www/html/var/www/common/polyfill/main.es
[rewrite:trace3] [initial/redir#1] [perdir /var/www/] strip per-dir prefix: /var/www/html/var/www/common/polyfill/main.es -> html/var/www/common/polyfill/main.es
[rewrite:trace3] [initial/redir#1] [perdir /var/www/] applying pattern '^([^/]+/[^.]+)$' to uri 'html/var/www/common/polyfill/main.es'
[rewrite:trace1] [initial/redir#1] [perdir /var/www/] pass through /var/www/html/var
[rewrite:trace3] [initial/redir#2] [perdir /var/www/] strip per-dir prefix: /var/www/html/error -> html/error
[rewrite:trace3] [initial/redir#2] [perdir /var/www/] applying pattern '^([^/]+/[^.]+)$' to uri 'html/error'
[rewrite:trace5] [initial/redir#2] map lookup FAILED: map=adjust-extension key=html/error
[rewrite:trace2] [initial/redir#2] [perdir /var/www/] rewrite 'html/error' -> 'html/error'
[rewrite:trace3] [initial/redir#2] [perdir /var/www/] add per-dir prefix: html/error -> /var/www/html/error
[rewrite:trace1] [initial/redir#2] [perdir /var/www/] initial URL equal rewritten URL: /var/www/html/error [IGNORING REWRITE]

http://example.jp/polyfill/main にアクセスしたときのrewriteログを整理すると以上のようになっています。[INTERNAL REDIRECT]が起こる前までは意図通りの結果になっているのですが、直後のadd path info postfix: /var/www/html/varでパスが壊れています。

なぜこのような処理がされるのでしょうか。add path info postfix: /var/www/html/varを回避するには、どういう設定を記述すればよいのでしょうか。


【2013-11-03追記】

教えていただいた内容をもとに修正した設定ファイルを追記しておきます。

RewriteEngine on

# 標準入力の「フルパス,プレフィックスを取り除いたパス」から拡張子付きのファイルを探し、最初に見つかったファイルのパスからプレフィックスを取り除いて返す
RewriteMap adjust-extension "prg:/usr/bin/perl -e '$| = 1; while (<STDIN>) { chomp($_); my @params = split(/,/, $_); my $path; if ($#params == 1) { ($path) = glob(@params[0] . \\'.*\\'); if ($path) { $path = substr($path, length(@params[0]) - length(@params[1])); } } print(($path || \\'NULL\\') . \"\\\\x0A\"); }'"

# 拡張子無しのURIでアクセスできるようにする
<Directory /var/www>
    RewriteOptions Inherit
    RewriteRule ^([^.]+)$ ${adjust-extension:%{REQUEST_FILENAME},$1|$1}
</Directory>

Alias /polyfill /var/www/common/polyfill
<Directory /var/www/common/polyfill>
    RewriteBase /polyfill/
</Directory>

<VirtualHost *:80>
    ServerName default-virtual-host.invalid
    Redirect permanent / http://example.jp/
</VirtualHost>
<VirtualHost *:80>
    ServerName example.jp
    DocumentRoot /var/www/html
    RewriteOptions Inherit
</VirtualHost>
<VirtualHost *:80>
    ServerName subdomain.example.jp
    DocumentRoot /var/www/subdomain
    RewriteOptions Inherit
    AliasMatch ^/((?:index|style)(?:\.[^/]+)?)$ /var/www/share/subdomain/core/$1
    <Directory /var/www/share/subdomain/core>
        RewriteBase /
        Require all granted
    </Directory>
</VirtualHost>

回答

2.4 は使ったことないですが、2.2 なら alias と rewrite を絡める場合は、<Directory> ディレクティブや .htaccess に RewriteBase が必要でした。

<Directory /var/www>
    RewriteRule ^([^/]+/[^.]+)$ ${adjust-extension:$1|$1}
</Directory>
<Directory /var/www/common/polyfill>
    RewriteBase /polyfill
    RewriteRule ^([^/]+/[^.]+)$ ${adjust-extension:$1|$1}
</Directory>

ログの前半部分は次のような意味だと思います。

strip per-dir prefix: /var/www/common/polyfill/main -> common/polyfill/main

<Directory> に指定された /var/www を除去

applying pattern '^([^/]+/[^.]+)$' to uri 'common/polyfill/main'
map lookup OK: map=adjust-extension key=common/polyfill/main -> val=common/polyfill/main.es
rewrite 'common/polyfill/main' -> 'common/polyfill/main.es'

RewriteRule と RewriteMap で common/polyfill/main.es にリライト

add per-dir prefix: common/polyfill/main.es -> /var/www/common/polyfill/main.es

RewriteBase を追加(未指定なら DocumentRoot と RequestURL から導出されるが、DocumentRoot 外に Alias していると導出できないのでこのようになる)

internal redirect with /var/www/common/polyfill/main.es [INTERNAL REDIRECT]

/var/www/common/polyfill/main.es へ内部リダイレクト(要するに http://example.jp/var/www/common/polyfill/main.es)

編集 履歴 (0)
  • ありがとうございます。内部リダイレクト時のパスはドキュメントルートからのパスなんですね。DocumentRoot 外の Alias 先も含めて RewriteRule を統一することはできないでしょうか。 -
  • できなさそうに思います・・・ -
  • わかりました。rewriteの仕組みについてわかりやすく解説していただき、どうもありがとうございました。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。