【发布时间】:2011-09-15 13:54:24
【问题描述】:
我编写了一个 Web 应用程序,我在专用服务器下运行该 Web 应用程序来托管该 Web 应用程序。此 Web 应用程序的实例在不同的域中可用,并且每个域都有自己的 Web 应用程序文件副本,允许根据需要进行自定义。
我在 Debian Squeeze 下运行 Apache/2.2.16。
我在 VirtualHost 指令下进行所有配置,不使用 .htaccess 文件。
为了简化 apache 配置,我想维护一个这样的 Directory 指令:
<Directory "/srv/www/*/public/">
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteCond %{REQUEST_URI} !=/robots.txt
RewriteRule ^(.+)$ /index.php?q=$1 [L,QSA]
</Directory>
但是,RewriteRule 会产生错误的结果,因为在使用通配符 Directory 值时,它无法去除每个目录的前缀。这是重写日志的输出:
[rid#b9832078/initial] (3) [perdir /srv/www/*/public/] applying pattern '^(.+)$' to uri '/srv/www/domain1/public/login'
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-f' => matched
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-d' => matched
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/login' pattern='!=/favicon.ico' => matched
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/login' pattern='!=/robots.txt' => matched
[rid#b9832078/initial] (2) [perdir /srv/www/*/public/] rewrite '/srv/www/domain1/public/login' -> '/index.php?q=/srv/www/domain1/public/login'
[rid#b9832078/initial] (3) split uri=/index.php?q=/srv/www/domain1/public/login -> uri=/index.php, args=q=/srv/www/domain1/public/login
[rid#b9832078/initial] (1) [perdir /srv/www/*/public/] internal redirect with /index.php [INTERNAL REDIRECT]
[rid#b9847440/initial/redir#1] (3) [perdir /srv/www/*/public/] applying pattern '^(.+)$' to uri '/srv/www/domain1/public/index.php'
[rid#b9847440/initial/redir#1] (4) [perdir /srv/www/*/public/] RewriteCond: input='/srv/www/domain1/public/index.php' pattern='!-f' => not-matched
[rid#b9847440/initial/redir#1] (1) [perdir /srv/www/*/public/] pass through /srv/www/domain1/public/index.php
问题在于 RewriteRule 'uri' 是文件系统路径而不是 url 路径,这导致查询字符串不正确:q=/srv/www/domain1/public/login
像这样显式指定目录路径:
<Directory "/srv/www/domain1/public/">
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteCond %{REQUEST_URI} !=/robots.txt
RewriteRule ^(.+)$ /index.php?q=$1 [L,QSA]
</Directory>
工作得很好,这是显示正确行为的重写日志的输出(不同之处在于新的第一行为其余的重写提供了正确的输入,从而产生了正确的查询字符串:q =登录):
[rid#b9868048/initial] (3) [perdir /srv/www/domain1/public/] strip per-dir prefix: /srv/www/domain1/public/login -> login
[rid#b9868048/initial] (3) [perdir /srv/www/domain1/public/] applying pattern '^(.+)$' to uri 'login'
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-f' => matched
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-d' => matched
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/login' pattern='!=/favicon.ico' => matched
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/login' pattern='!=/robots.txt' => matched
[rid#b9868048/initial] (2) [perdir /srv/www/domain1/public/] rewrite 'login' -> '/index.php?q=login'
[rid#b9868048/initial] (3) split uri=/index.php?q=login -> uri=/index.php, args=q=login
[rid#b9868048/initial] (1) [perdir /srv/www/domain1/public/] internal redirect with /index.php [INTERNAL REDIRECT]
[rid#b987d5f8/initial/redir#1] (3) [perdir /srv/www/domain1/public/] strip per-dir prefix: /srv/www/domain1/public/index.php -> index.php
[rid#b987d5f8/initial/redir#1] (3) [perdir /srv/www/domain1/public/] applying pattern '^(.+)$' to uri 'index.php'
[rid#b987d5f8/initial/redir#1] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/srv/www/domain1/public/index.php' pattern='!-f' => not-matched
[rid#b987d5f8/initial/redir#1] (1) [perdir /srv/www/domain1/public/] pass through /srv/www/domain1/public/index.php
我预计我遇到了 Apache 的错误,但如果不是这样,我做错了什么?
虽然我很欣赏将方法更改为另一种可行解决方案的意见,但我会接受以我采用的方法解决它的答案(例如,不使用 .htaccess),除非可以证明这种方法是不可解决的。
那么,在通配符目录中使用时,RewriteCond/Rules 有什么需要更改的吗?
好奇的旁注:为了进一步简化,我使用单个使用 VirtualDocumentRoot 的 VirtualHost - 但这无关紧要,因为使用“DocumentRoot”和在单个域下测试会复制此问题。
编辑
好的,我已经根据 regilero 的回答重新审视了这个问题,这就是发生的情况 - 将 Rewrite 按原样移出 Directory 会导致查询字符串出现轻微的初始问题从“login”更改为“/login”,通过将 RewriteRule 修改为:RewriteRule ^/(.+)$ /index.php?q=$1 [L,QSA] 修复了我之前的“莫名其妙地失败”评论。
随后,所有静态文件都无法加载,这是显示此问题的重写日志:
[rid#b7bc7fa0/initial] (2) init rewrite engine with requested uri /login
[rid#b7bc7fa0/initial] (3) applying pattern '^/(.+)$' to uri '/login'
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!-f' => matched
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!-d' => matched
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!=/favicon.ico' => matched
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!=/robots.txt' => matched
[rid#b7bc7fa0/initial] (2) rewrite '/login' -> '/index.php?q=login'
[rid#b7bc7fa0/initial] (3) split uri=/index.php?q=login -> uri=/index.php, args=q=login
[rid#b7bc7fa0/initial] (2) local path result: /index.php
[rid#b7bc7fa0/initial] (2) prefixed with document_root to /srv/www/domain1/public/index.php
[rid#b7bc7fa0/initial] (1) go-ahead with /srv/www/domain1/public/index.php [OK]
[rid#b7be6b80/initial] (2) init rewrite engine with requested uri /static/css/common.css
[rid#b7be6b80/initial] (3) applying pattern '^/(.+)$' to uri '/static/css/common.css'
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!-f' => matched
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!-d' => matched
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!=/favicon.ico' => matched
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!=/robots.txt' => matched
[rid#b7be6b80/initial] (2) rewrite '/static/css/common.css' -> '/index.php?q=static/css/common.css'
[rid#b7be6b80/initial] (3) split uri=/index.php?q=static/css/common.css -> uri=/index.php, args=q=static/css/common.css
[rid#b7be6b80/initial] (2) local path result: /index.php
[rid#b7be6b80/initial] (2) prefixed with document_root to /srv/www/domain1/public/index.php
[rid#b7be6b80/initial] (1) go-ahead with /srv/www/domain1/public/index.php [OK]
但就像我在对 regilero 的回答的评论中所说的那样,这可以通过在 RewriteCond 指令 TestString 前加上 %{DOCUMENT_ROOT} 来解决。但是,使用 VirtualDocumentRoot 时,使用 %{DOCUMENT_ROOT} 不起作用。
在我看来,%{DOCUMENT_ROOT} 前缀不是必须的。
编辑
请求文件名
匹配的文件或脚本的完整本地文件系统路径 请求,如果这已经由服务器当时确定 引用了 REQUEST_FILENAME。否则,例如在使用时 虚拟主机上下文,与 REQUEST_URI 的值相同。
这解释了 DOCUMENT_ROOT 前缀的必要性。
我已将重写规则更新为:
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteCond %{REQUEST_URI} !=/robots.txt
RewriteCond %{REQUEST_URI} !^/static/
RewriteRule ^/(.+)$ /index.php?q=$1 [PT,L,QSA]
哪个工作正常(注意:PT 标志是必要的,以避免在使用 VirutalDocumentRoot 时过早地将 url 路径转换为文件系统路径)。这里的主要行为变化是应用程序的所有入口点都需要 RewriteCond - 类似于 /static 行。
编辑
这是我在任何 Directory 指令之外的 VirtualHost 中的 Rewrite 指令的最终化身:
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/static/
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteCond %{REQUEST_URI} !=/robots.txt
RewriteRule ^/(.+)$ /index.php?q=$1 [NS,PT,L,QSA]
RewriteRule ^/$ /index.php [NS,PT,L,QSA]
我添加了NS 标志以避免额外的内部评估,并添加了第二个RewriteRule 指令以支持使用mod_dir 和DirectoryIndex。我的应用程序不希望根 url 有 q= 参数,否则,如果应用程序更新为接受根 url 的空 q= 参数,则单个 RewriteRule 或 RewriteRule ^/(.*)$ /index.php?q=$1 [NS,PT,L,QSA] 就足够了。我将来可能会这样做。
【问题讨论】:
标签: mod-rewrite apache2