如何让RewriteRule(.htaccess)的[L]标志真正起作用?

时间:2010-09-03 23:16:04

标签: .htaccess mod-rewrite

对新手:在尝试全面描述我的问题并说出我的问题时,我制作了大量的文字。如果你不想阅读整篇文章,那么我的观察(阅读“证据”)[L]标志不起作用错误概念,它从中产生了,位于< strong>其他观察部分。为什么我误解了明显的行为在我的答案以及给定问题的解决方案中有所描述。

设置


我的.htaccess文件中有以下代码:

# disallow directory indexing
Options -Indexes

# turn mod_rewrite on
Options +FollowSymlinks
RewriteEngine on

# allow access to robots file
RewriteRule ^robots.txt$ robots.txt [NC,L]

# mangle core request handler address
RewriteRule ^core/(\?.+)?$ core/handleCoreRequest.php$1 [NC,L]

# mangle web file adresses (move them to application root folder)
# application root folder serves as application GUI address
RewriteRule ^$ web/index.html [L]
# allow access to images
RewriteRule ^(images/.+\.(ico|png|bmp|jpg|gif))$ web/$1 [NC,L]
# allow access to stylesheets
RewriteRule ^(css/.+\.css)$ web/$1 [NC,L]
# allow access to javascript
RewriteRule ^(js/.+\.js)$ web/$1 [NC,L]
# allow access to library scripts, styles and images
RewriteRule ^(lib/js/.+\.js)$ web/$1 [NC,L]
RewriteRule ^(lib/css/.+\.css)$ web/$1 [NC,L]
RewriteRule ^(lib/(.+/)?images/.+\.(ico|png|bmp|jpg|gif))$ web/$1 [NC,L]

# redirect all other requests to application address
# RewriteRule ^(.*)$ /foo/ [R]

我的网络应用程序(及其.htaccess文件)位于foo的{​​{1}}子文件夹中(从浏览器访问DOCUMENT_ROOT)。它的PHP核心部分位于http://localhost/foo/,JavaScript GUI部分位于foo/core。从上面的代码可以看出,我想只允许访问单核心脚本,该脚本处理来自GUI和“安全”Web文件的所有请求,并将所有其他请求重定向到基本应用程序地址(最后一个注释指令)。


问题


行为

直到我通过取消注释最后一个重定向指令来尝试最后一部分才有效。如果我再评论一些行,那么相应的页面部分就会停止工作等等。

然而,当我取消注释最后一行时,只有当所有先前规则的匹配失败时才会执行(至少这是我所理解的),页面进入重定向周期(Firefox会抛出错误页面,例如“This page isn”正确重定向“),因为它会一次又一次地重定向到foo/web

问题

我不明白的是这个规则的处理:

http://localhost/foo/

具体是RewriteRule ^$ web/index.html [L]标志。 该标志显然对我不起作用。当最后一行被注释时,它正确地重定向,但是当我取消注释它时,它总是被处理,即使重写应该在[L]标志上停止。有人有任何想法吗?

另外,在旁注中,我很高兴知道为什么我的下一次尝试修复它也不起作用:

[L]

这实际上根本不起作用。即使我删除了最后一行,它仍然没有正确地重定向。如果第一个示例中的重定向不起作用,如何在第二个示例中起作用?

如果有人知道如何实际调试这些指令,那对我来说也是非常有益的。我花了好几个小时就没有任何可能出错的线索。


其他观察


在尝试了bbadour给出的建议之后(不是我之前没有尝试过,但现在我有了第二个意见,我又给了它一个镜头)并且它没有用,我想出了以下观察。通过重写最后一行:

RewriteEngine on
RewriteRule ^core/(\?.+)?$ core/handleCoreRequest.php$1 [NC,L]
RewriteRule ^(.*)$ web/$1 [L]
RewriteRule ^.*$ /foo/ [L]

或者

RewriteRule ^(.*)$ /foo/?uri=$1 [R,L]

并且使用Firebug的Net面板,我发现了更多的证据,[L]标志显然没有按照前面提到的RewriteRule ^(.*)$ /foo/?uri=%{REQUEST_URI} [R,L] 规则中的预期工作(从现在开始称之为“规则”)。在第一种情况下,我得到RewriteRule ^$ web/index.html [L],在第二种情况下[...]uri=web/index.html。这意味着THE RULE被执行(重写^ $到web / index.html),但重写并不止于此。还有什么想法吗?

3 个答案:

答案 0 :(得分:12)

经过数小时的搜索和测试,我终于找到了真正的问题和解决方案。希望当他们遇到同样的问题时,这也会帮助其他人。

观察到的行为的原因


.htaccess文件在每次重定向后处理(即使没有[R]标志)

这意味着在处理RewriteRule ^$ web/index.html [L]之后,mod_rewrite会正确停止重写,转到文件末尾,正确地重定向到/foo/web/index.html然后服务器开始处理.htaccess文件对于新位置,这是同一个文件。现在只有最后一次重写规则匹配并重定向回/foo/(这次使用[R],因此可以在浏览器中观察到重定向)...并再次处理.htaccess文件,并再次处理...

为了清晰起见,再一次:因为只能观察到硬重定向,所以似乎忽略了[L]标志,但事实并非如此。相反,.htaccess会在/foo//foo/web/index.html之间来回重定向两次。


解决方案


禁止直接访问子文件夹

要将子目录虚拟移动到应用程序根目录,必须使用其他复杂的条件重写。变量THE_REQUEST可用于区分硬重定向和软重定向:

RewriteCond %{THE_REQUEST} ^GET\ /foo/web/
RewriteRule ^web/(.*) /foo/$1 [L,R]

要匹配此重写规则,必须应用两个条件。首先,在第二行,“本地URI”必须以web/开头(与绝对网络URI /foo/web/对应)。其次,在第一行,实际请求URI也必须以/foo/web/开头。这意味着,规则仅在直接从浏览器请求web/子文件夹中的文件时匹配,在这种情况下,我们想要进行硬重定向。

重定向到允许从root到子文件夹的内容(软)

RewriteCond $1 !^web/
RewriteCond $1 ^(.+\.(html|css|js|ico|png|bmp|jpg|gif))?$
RewriteRule ^(.*)$ web/$1 [L,NC]

我们希望仅在我们尚未完成时才重定向到允许的内容,因此是第一个条件。第二个条件指定允许内容的掩码。与此掩码匹配的任何内容都将被轻柔地重定向,如果内容不存在,则可能返回404错误。

隐藏不在子文件夹中或不允许的所有内容

RewriteRule !^web/ /foo/ [L,R]

这将为不以web/开头的所有URI执行硬重定向到应用程序根目录(请记住,此时只能以web/开头的请求是允许内容的内部重定向。


真实的例子


使用上述解决方案提示后,我的“问题”中显示的代码逐渐转化为以下内容:

# disallow directory indexing
Options -Indexes

# turn mod_rewrite on
Options +FollowSymlinks
RewriteEngine on

# allow access to robots file
RewriteRule ^robots.txt$ - [NC,L]

# mangle core request handler address
# disallow direct access to core request handler
RewriteCond %{THE_REQUEST} !^(GET|POST)\ /asm/core/handleCoreRequest.php
RewriteRule ^core/handleCoreRequest.php$ - [L]
# allow access to request handler under alias
RewriteRule ^core/$ core/handleCoreRequest.php [NC,QSA,L]

# mangle GUI files adressing (move to application root folder)
# disallow direct access to GUI subfolder
RewriteCond %{THE_REQUEST} ^GET\ /foo/web/
RewriteRule ^web/(.*) /foo/$1 [L,R]
# allow access only to correct filetypes in appropriate locations
RewriteCond $1 ^$ [OR]
RewriteCond $1 ^(images/.+\.(ico|png|bmp|jpg|gif))$ [OR]
RewriteCond $1 ^(css/.+\.css)$ [OR]
RewriteCond $1 ^(js/.+\.js)$ [OR]
RewriteCond $1 ^(lib/js/.+\.js)$ [OR]
RewriteCond $1 ^(lib/css/.+\.css)$ [OR]
RewriteCond $1 ^(lib/(.+/)?images/.+\.(ico|png|bmp|jpg|gif))$
RewriteRule ^(.*)$ web/$1 [L,NC]

# hide all files not in GUI subfolder that are not whitelisted above
RewriteRule !^web/ /foo/ [L,R]


我不喜欢这种方法是应用程序根文件夹必须在.htaccess文件中硬编码(据我所知),因此必须在应用程序安装时生成文件,而不是简单地复制。

答案 1 :(得分:0)

要进行调试,请尝试简化正则表达式,以及您要求的网址(您想要匹配的完整网址的一部分),并查看它是否正常工作,现在一步一步地向测试网址中的正则表达式添加更多位,直到找到正常停止工作的地方。

答案 2 :(得分:0)

尝试使用:

RewriteRule ^(.*)$ /foo/ [R,L]

如果它仍然循环播放,请在其前面放置RewriteCond以跳过规则/foo/