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

时间:2022-08-10 10:51:10

To newcomers: While trying to comprehensively describe my problem and phrase my questions I produced huge ammount of text. If you don't want to read the whole thing, my observations about (read "proof of") [L] flag not working the misconception, from which it all sprung, is located in Additional observations section. Why I misunderstood apparent behaviour is described in my Answer as well as solution to given problem.

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

Setup


I have following code in my .htaccess file:

我的.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]

My web application (and its .htaccess file) is located in foo subfolder of DOCUMENT_ROOT (accessed from browser as http://localhost/foo/). It has PHP core part located in foo/core and JavaScript GUI part located in foo/web. As can be seen from the code above, I want to allow access only to single core script that handles all requests from GUI and to 'safe' web files and redirect all other requests to base application address (last commented directive).

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


Problem


Behaviour

It works until I try the last part by uncommenting the last redirecting directive. If I comment some more lines, the appropriate page parts stop working, etc.

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

However, when I uncomment last line, which should be performed only when matching of all previous rules fails (at least that's what I understand), page goes into redirection cycle (Firefox throws error page with something like "This page isn't redirecting properly"), because it's redirecting to http://localhost/foo/ again and again and again, forever.

但是,当我取消注释最后一行时,只有在所有先前规则的匹配失败时才会执行(至少这是我所理解的),页面进入重定向循环(Firefox会抛出错误页面,例如“此页面未正确重定向” “),因为它会一次又一次地重定向到http:// localhost / foo /,永远。

Questions

What I don't understand is this processing of this rule:

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

RewriteRule ^$ web/index.html [L],

RewriteRule ^ $ web / index.html [L],

specifically the [L] flag. The flag apparently doesn't work for me. When the last line is commented, it correctly redirects, but when I uncomment it, it is always processed, even though rewriting should stop on [L] flag. Anyone got any ideas?

特别是[L]标志。国旗显然不适合我。当最后一行被注释时,它正确地重定向,但是当我取消注释它时,它总是被处理,即使重写应该在[L]标志上停止。有人有任何想法吗?

Also, on a sidenote, I'd be thrilled to know why my following attempt at fixing it doesn't work either:

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

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

This actually doesn't work at all. Even if I remove the last line, it still doesn't redirect anything correctly. How does the redirecting work in the first example, if it doesn't work in the second?

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

It would also be of great benefit to me, if anybody knew any way to actually debug these directives. I spend hours on this without even the slightest clue what could possibly be wrong.

如果有人知道如何实际调试这些指令,那对我来说也会有很大的好处。我花了几个小时就没有丝毫的线索,这可能是错的。


Additional observations


After trying the advice given by bbadour (not that I haven't tried it before, but now that I had a second opinion, I gave it another shot) and it didn't work, I've come up with the following observation. By rewriting last line to this:

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

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

or this

或这个

RewriteRule ^(.*)$ /foo/?uri=%{REQUEST_URI} [R,L]

and using Firebug's Net panel, I found out more evidence, that the [L] flag is clearly not working as expected in the previously mentioned RewriteRule ^$ web/index.html [L] rule (let's call it THE RULE from now on). In first case I get [...]uri=web/index.html, in second case [...]uri=/foo/web/index.html. That means that THE RULE gets executed (rewrites ^$ to web/index.html), but the rewriting doesn't stop there. Any more ideas, please?

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

3 个解决方案

#1


12  

After hours of searching and testing, I finally found the real problem and solution. Hopefully this will help somebody else too, when they come across the same problem.

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

Cause of observed behavior


.htaccess file is processed after every redirect (even without [R] flag),

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

which means that after the RewriteRule ^$ web/index.html [L] is processed, mod_rewrite correctly stops rewriting, goes to the end of the file, redirects correctly to /foo/web/index.html, and then the server starts processing .htaccess file for the new location, which is the same file. Now only the last rewrite rule matches and redirects back to /foo/ (this time with [R], so the redirect can be observed in browser) ... and the .htaccess file is processed again, and again, and again...

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

Once more for clarity: Because only the hard redirects can be observed, it seems like the [L] flag is ignored, but it is not so. Instead, the .htaccess is processed two times redirecting back and forth between /foo/ and /foo/web/index.html.

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


Solution


Disallow direct access to subfolder

To virtually move subdirectory to application root directory, additional complex conditional rewrites must be used. Variable THE_REQUEST is useful for distinguishing between hard and soft redirects:

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

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

For this rewrite rule to be matched, two conditions must apply. First, on second line, the "local URI" must start with web/ (which corresponds with absolute web URI /foo/web/). Second, on first line, the real request URI must start with /foo/web/ too. Together this means, that the rule only matches when the file inside the web/ subfolder is requested directly from the browser, in which case we want to do a hard redirect.

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

Redirect to allowed content from root to subfolder (soft)

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

We want to redirect to allowed content only if we haven't done it already, hence the first condition. Second condition specifies mask for allowed content. Anything matching this mask will be softly redirected, possibly returning 404 error if the content doesn't exist.

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

Hide all content not in subfolder or not allowed

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

This will do a hard redirect to application root for all URIs not beginning with web/ (and remember, only requests that can begin with web/ at this point are internal redirects for allowed content.

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


Real example


My code shown in my "question" after using solution tips mentioned above gradually transformed into the following:

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

# 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]


What I don't like about this approach is that the application root folder must be hardcoded in .htaccess file (as far as I know), so the file must be generated on application install, not simply copied.

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

#2


0  

To debug, try simplifying your regex, and the url you ask for (a part of the full url you wanna match), and see if it's working, now step by step, add more bits to the regex adn the testing url, till you find where things are stopping to work properly.

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

#3


0  

Try using:

尝试使用:

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

If it still loops, put a RewriteCond in front of it to skip the rule if it is already /foo/

如果它仍然循环,在它前面放一个RewriteCond来跳过规则,如果它已经/ foo /

#1


12  

After hours of searching and testing, I finally found the real problem and solution. Hopefully this will help somebody else too, when they come across the same problem.

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

Cause of observed behavior


.htaccess file is processed after every redirect (even without [R] flag),

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

which means that after the RewriteRule ^$ web/index.html [L] is processed, mod_rewrite correctly stops rewriting, goes to the end of the file, redirects correctly to /foo/web/index.html, and then the server starts processing .htaccess file for the new location, which is the same file. Now only the last rewrite rule matches and redirects back to /foo/ (this time with [R], so the redirect can be observed in browser) ... and the .htaccess file is processed again, and again, and again...

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

Once more for clarity: Because only the hard redirects can be observed, it seems like the [L] flag is ignored, but it is not so. Instead, the .htaccess is processed two times redirecting back and forth between /foo/ and /foo/web/index.html.

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


Solution


Disallow direct access to subfolder

To virtually move subdirectory to application root directory, additional complex conditional rewrites must be used. Variable THE_REQUEST is useful for distinguishing between hard and soft redirects:

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

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

For this rewrite rule to be matched, two conditions must apply. First, on second line, the "local URI" must start with web/ (which corresponds with absolute web URI /foo/web/). Second, on first line, the real request URI must start with /foo/web/ too. Together this means, that the rule only matches when the file inside the web/ subfolder is requested directly from the browser, in which case we want to do a hard redirect.

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

Redirect to allowed content from root to subfolder (soft)

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

We want to redirect to allowed content only if we haven't done it already, hence the first condition. Second condition specifies mask for allowed content. Anything matching this mask will be softly redirected, possibly returning 404 error if the content doesn't exist.

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

Hide all content not in subfolder or not allowed

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

This will do a hard redirect to application root for all URIs not beginning with web/ (and remember, only requests that can begin with web/ at this point are internal redirects for allowed content.

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


Real example


My code shown in my "question" after using solution tips mentioned above gradually transformed into the following:

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

# 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]


What I don't like about this approach is that the application root folder must be hardcoded in .htaccess file (as far as I know), so the file must be generated on application install, not simply copied.

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

#2


0  

To debug, try simplifying your regex, and the url you ask for (a part of the full url you wanna match), and see if it's working, now step by step, add more bits to the regex adn the testing url, till you find where things are stopping to work properly.

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

#3


0  

Try using:

尝试使用:

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

If it still loops, put a RewriteCond in front of it to skip the rule if it is already /foo/

如果它仍然循环,在它前面放一个RewriteCond来跳过规则,如果它已经/ foo /