1

I can't understand why redirect depends on RewriteRule (not on RewriteCond).

My .htaccess:

Options +FollowSymLinks +SymLinksIfOwnerMatch

<IfModule mod_rewrite.c>
RewriteEngine on

RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*)$ true.txt

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ false.txt
</IfModule>

Root folder contains:

true.txt (contains 'true')
false.txt (contains 'false')
test.txt (contains 'test')

If I try to open test.txt I get true and if I try to open nonexist.txt i get true too.

Now I change my .htaccess:

...
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*)$ $1
...

And now if I try to open test.txt I get test and if I try to open nonexist.txt i get false.

UPDATE: Thanks for answers, I understood how it works but one problem still exists. If I try to check 'if file exists' in another directory it always returns false.

/files/test.txt
/script/.htaccess
/script/false.txt
/script/true.txt

now my .htaccess looks like

RewriteCond %{REQUEST_FILENAME} .*(true|false).*$
RewriteRule .* - [S=2]

RewriteCond %{DOCUMENT_ROOT}/files/%{REQUEST_FILENAME} -f
RewriteRule ^(.*)$ true.txt [L]

RewriteCond %{DOCUMENT_ROOT}/files/%{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ false.txt [L]

I always get false. I also tried RewriteCond ../files/%{REQUEST_FILENAME} and also always get false result.

If I move test.txt in script folder then and change RewriteCond %{REQUEST_FILENAME} all works fine.

4

3 回答 3

7

这是因为 mod_rewrite 的工作方式:用户请求 test.txt,mod_rewrite 捕获请求并将 URI 重写为 false.txt,然后通过发送对 false.txt 的内部请求进行第二次传递,该请求被捕获并重写到 true.txt。然后进行第三次传递,请求被捕获并重写为 true.txt,但由于 URI 保持不变,因此不再进行传递。

这是相当违反直觉的,但这是有逻辑的。这是文档中的控制流程图:

mod_rewrite的控制流程图

[L] 标志通常被宣传为停止递归的灵丹妙药,但实际上它只是确保一旦请求与模式匹配,则执行停止并且在该 pass 中不会进行进一步处理,但内部请求无论如何都会被发送出去,所以第二次通过相同的规则集。仅当通过后 URI 未更改时,执行才会停止。

回复:更新 您的问题是, REQUEST_FILENAME 环境变量实际上包含一个路径(默认情况下是完整的文件系统路径,但有一些曲折),所以 %{DOCUMENT_ROOT}/files/%{REQUEST_FILENAME} 最终变得很可怕.

至于解决方案……嗯,我认为这很棘手。如果 .htaccess 在根目录下,会容易得多。我现在能想到的唯一解决方案是:

RewriteEngine on

RewriteCond %{REQUEST_URI} script/(.*)$
RewriteCond %{DOCUMENT_ROOT}/files/%1 -f
RewriteRule .* true.txt [L]

RewriteCond %{REQUEST_URI} !(true.txt)|(false.txt)
RewriteRule .* false.txt [L]

它相当丑陋,而且不是非常可扩展或便携。在第一个条件中,我得到文件的名称,在第二个条件中,我检查它是否存在,如果存在,则为真。其他一切都是假的。再说一次,如果文件目录也在 .htaccess 的范围内,那么它就更容易和更好了。

于 2012-07-04T10:40:29.663 回答
1
RewriteEngine on

RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_URI} !(true|false)\.txt$
RewriteRule .* true.txt [L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* false.txt [L]

注意第二个RewriteCond防止重写true.txt和false.txt文件,并L在规则上标记停止规则执行这些是为了防止规则循环

更新

%{REQUEST_FILENAME}是完整路径,因此如果你将它添加到某个路径,你会得到错误(它会尝试匹配这个,本质上是:/var/www/subfolder/var/www/filename.txt

要匹配另一个文件夹中的文件,您将需要匹配与 URI 部分...

以下是您的操作方法:

RewriteEngine on
RewriteBase /

RewriteCond %{REQUEST_URI} ^/([^/]+)$
RewriteCond %{DOCUMENT_ROOT}/files/%1 -f
RewriteRule .* files/$0 [L]
  • 第一个条件检查请求是否针对根目录中的某个文件名(它检查 uri 是否以 / 开头,但不再包含斜杠
  • 请注意,第一个条件将除斜线之外的所有内容都包含在括号中 - 此匹配的子模式将在稍后使用
  • 第二个条件确保保存在子模式中的文件%1(与第一个条件匹配)存在于子文件夹files/%{DOCUMENT_ROOT}
  • 如果两个规则都匹配,则将请求重写到该文件(通过子请求 - 浏览器不会被重定向)。
于 2012-07-04T10:20:21.913 回答
0

而不是使用“RewriteCond %{REQUEST_FILENAME} !-f”,您可以尝试:
“RewriteCond %{THE_REQUEST} !-U”,它会检查地址是否存在。
有时文件路径和提供文件的地址不同,导致前者无法使用。

例子:

RewriteEngine On
RewriteCond %{THE_REQUEST} !-U
RewriteRule ^(.*/media/.*)\.(gif|png|jpe?g)$ https://xyz.company.com$1.$2 [NC,L,R=301]
于 2013-09-20T13:04:12.277 回答