RewriteCond奇怪的行为(文件存在检查)

时间:2012-07-04 09:41:42

标签: apache .htaccess mod-rewrite

我无法理解为什么重定向依赖于RewriteRule(而不是RewriteCond)。

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

根文件夹包含:

  

true.txt(包含'true')
  false.txt(包含'false')
  test.txt(包含'test')

如果我尝试打开test.txt,我会true,如果我尝试打开nonexist.txt,我也会获得true

现在我改变了我的.htaccess:

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

现在,如果我尝试打开test.txt,我会test,如果我尝试打开nonexist.txt,我会false

更新:感谢您的回答,我了解它的工作原理,但仍然存在一个问题。 如果我尝试在另一个目录中检查'if file exists',它总是返回false

  

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

现在我的.htaccess看起来像

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]

我总是得到false。 我也尝试了RewriteCond ../files/%{REQUEST_FILENAME},并且始终获得false结果。

如果我在test.txt文件夹中移动script然后更改RewriteCond %{REQUEST_FILENAME}一切正常。

3 个答案:

答案 0 :(得分:7)

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

这是违反直觉的,但它有逻辑。以下是docs

的控制流程图

control flow diagram of mod_rewrite

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

重新:更新 你的问题是,REQUEST_FILENAME环境变量实际上拥有一个路径(默认情况下是完整的文件系统路径,但是有一些扭曲),所以%{DOCUMENT_ROOT} / files /%{REQUEST_FILENAME}最终会变得非常糟糕。

至于解决方案......好吧,我觉得这很棘手。如果.htaccess是root用户,那就容易多了。我现在能想到的唯一解决方案是:

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的范围内,那么它的大小就会更容易和更好。

答案 1 :(得分: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

要匹配其他文件夹中的文件,您需要匹配vs URI部分...

以下是您可以这样做的方法:

RewriteEngine on
RewriteBase /

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

答案 2 :(得分: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]