mod_rewrite的隐藏function

似乎有很多mod_rewrite线程在最近漂浮在它的某些方面工作有点混乱。 因此,我编写了一些关于常用function的注释,也许还有一些烦人的细微差别。

你使用mod_rewrite运行了哪些其他function/常见问题?

在哪里放置mod_rewrite规则

mod_rewrite规则可以放在httpd.conf文件中,也可以放在.htaccess文件中。 如果你有权访问httpd.conf ,那么在这里放置规则将会提供一个性能上的好处(因为规则只处理一次,而不是每次调用.htaccess文件)。

loggingmod_rewrite请求

可以从httpd.conf文件(包括<Virtual Host> )启用日志logging:

 # logs can't be enabled from .htaccess # loglevel > 2 is really spammy! RewriteLog /path/to/rewrite.log RewriteLogLevel 2 

常见用例

  1. 将所有请求汇集到一个点上:

     RewriteEngine on # ignore existing files RewriteCond %{REQUEST_FILENAME} !-f # ignore existing directories RewriteCond %{REQUEST_FILENAME} !-d # map requests to index.php and append as a query string RewriteRule ^(.*)$ index.php?query=$1 

    从Apache 2.2.16开始,你也可以使用FallbackResource

  2. 处理301/302redirect:

     RewriteEngine on # 302 Temporary Redirect (302 is the default, but can be specified for clarity) RewriteRule ^oldpage\.html$ /newpage.html [R=302] # 301 Permanent Redirect RewriteRule ^oldpage2\.html$ /newpage.html [R=301] 

    注意 :外部redirect隐含302redirect:

     # this rule: RewriteRule ^somepage\.html$ http://google.com # is equivalent to: RewriteRule ^somepage\.html$ http://google.com [R] # and: RewriteRule ^somepage\.html$ http://google.com [R=302] 
  3. 强制SSL

     RewriteEngine on RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://example.com/$1 [R,L] 
  4. 常用标志:

    • [R][redirect] – 强制redirect(默认为302临时redirect)
    • [R=301][redirect=301] – 强制301永久redirect
    • [L][last] – 停止重写过程(请参阅以下常见陷阱注意事项)
    • [NC][nocase] – 指定匹配应该不区分大小写

    使用长forms的标志通常更具可读性,并且可以帮助其他人稍后阅读您的代码。

    你可以用逗号分隔多个标志:

     RewriteRule ^olddir(.*)$ /newdir$1 [L,NC] 

常见的陷阱

  1. 混合mod_alias风格的redirect与mod_rewrite

     # Bad Redirect 302 /somepage.html http://example.com/otherpage.html RewriteEngine on RewriteRule ^(.*)$ index.php?query=$1 # Good (use mod_rewrite for both) RewriteEngine on # 302 redirect and stop processing RewriteRule ^somepage.html$ /otherpage.html [R=302,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # handle other redirects RewriteRule ^(.*)$ index.php?query=$1 

    注意 :你可以将mod_aliasmod_rewrite混合使用,但是它涉及到的工作多于上面处理基本redirect的工作。

  2. 上下文影响语法

    .htaccess文件中,重写规则模式中不使用前导斜杠:

     # given: GET /directory/file.html # .htaccess # result: /newdirectory/file.html RewriteRule ^directory(.*)$ /newdirectory$1 # .htaccess # result: no match! RewriteRule ^/directory(.*)$ /newdirectory$1 # httpd.conf # result: /newdirectory/file.html RewriteRule ^/directory(.*)$ /newdirectory$1 # Putting a "?" after the slash will allow it to work in both contexts: RewriteRule ^/?directory(.*)$ /newdirectory$1 
  3. [L]不是最后的! (有时)

    [L]标志停止处理通过规则集的任何进一步的重写规则。 但是,如果在该通道中修改了URL,并且您在.htaccess上下文或<Directory>部分中,那么您修改的请求将会通过URLparsing引擎再次传回。 在下一个阶段,这次可能会有不同的规则。 如果你不明白这一点,通常看起来你的[L]旗帜没有任何效果。

     # processing does not stop here RewriteRule ^dirA$ /dirB [L] # /dirC will be the final result RewriteRule ^dirB$ /dirC 

    我们的重写日志显示规则运行两次,URL更新两次:

     rewrite 'dirA' -> '/dirB' internal redirect with /dirB [INTERNAL REDIRECT] rewrite 'dirB' -> '/dirC' 

    如果你真的想停止所有进一步的规则处理(和后续的通行证),最好的方法是使用[END]标志( 请参阅Apache文档 )而不是[L]标志。 但是, [END]标志仅适用于Apache v2.3.9 + ,因此如果v2.2或更低版本,则只能使用[L]标志。

    对于较早的版本,您必须依赖RewriteCond语句来防止在URLparsing引擎的后续传递中匹配规则。

     # Only process the following RewriteRule if on the first pass RewriteCond %{ENV:REDIRECT_STATUS} ^$ RewriteRule ... 

    或者你必须确保你的RewriteRule是在一个上下文(即httpd.conf ),不会导致你的请求被重新分析。

如果你需要“阻止”在.htaccess中发生的内部redirect/重写,请看一下

 RewriteCond %{ENV:REDIRECT_STATUS} ^$ 

条件, 这里讨论 。

与RewriteBase的交易:

你几乎总是需要设置RewriteBase。 如果你不这样做,apache会猜测你的base是你的目录的物理磁盘path。 所以从这开始吧:

 RewriteBase / 

其他陷阱:

1-有时,禁用MultiViews是一个好主意

 Options -MultiViews 

我不是所有的MultiViewsfunction的经文,但我知道,它激活了我的mod_rewrite规则,因为它的一个属性是试图'猜测'它认为我正在寻找一个文件的扩展名。

我会解释一下:假设你在你的web dir,file1.php和file2.php中有两个php文件,你将这些条件和规则添加到你的.htaccess中:

 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ file1.php/$1 

您认为所有不匹配文件或目录的URL将被file1.php抓取。 惊喜! url http:// myhost / file2 / somepath不符合此规则。 相反,你被拿进file2.php。

发生了什么事是MultiViews自动猜测你实际需要的URL是http://myhost/file2.php/somepath ,很高兴地把你带到那里。

现在,你不知道刚刚发生了什么事,而你正在质疑所有你认为对mod_rewrite了解的东西。 然后,你开始玩弄规则,试图理解这种新情况背后的逻辑,但越是你testing的感觉越less。

好的,简而言之,如果您希望mod_rewrite以接近逻辑的方式工作,closuresMultiViews是一个正确的方向。

2-启用FollowSymlinks

 Options +FollowSymLinks 

那个,我真的不知道细节,但是我多次提到过,所以就这样做吧。

等式可以用下面的例子来完成:

 RewriteCond %{REQUEST_URI} ^/(server0|server1).*$ [NC] # %1 is the string that was found above # %1<>%{HTTP_COOKIE} concatenates first macht with mod_rewrite variable -> "test0<>foo=bar;" #RewriteCond search for a (.*) in the second part -> \1 is a reference to (.*) # <> is used as an string separator/indicator, can be replaced by any other character RewriteCond %1<>%{HTTP_COOKIE} !^(.*)<>.*stickysession=\1.*$ [NC] RewriteRule ^(.*)$ https://notmatch.domain.com/ [R=301,L] 

dynamic负载平衡:

如果使用mod_proxy来平衡系统,可以添加一个dynamic范围的工作服务器。

 RewriteCond %{HTTP_COOKIE} ^.*stickysession=route\.server([0-9]{1,2}).*$ [NC] RewriteRule (.*) https://worker%1.internal.com/$1 [P,L] 

更好地理解[L]国旗是为了。 [L]标志最后一个,你只需要明白什么会导致你的请求再次通过URLparsing引擎被路由。 从文档( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l )(重点是我的):

[L]标志导致mod_rewrite停止处理规则集。 在大多数情况下,这意味着如果规则匹配,则不会处理更多的规则。 这对应于Perl中的最后一个命令,或C中的break命令。使用此标志来指示当前规则应立即应用,而不考虑其他规则。

如果您在.htaccess文件或<Directory>部分中使用RewriteRule ,那么了解规则的处理方式非常重要。 这个简单的forms是,一旦规则被处理完毕, 重写的请求被传递给URLparsing引擎,以便完成它的任务。 有可能在处理重写的请求时,可能会再次遇到.htaccess文件或<Directory>部分,因此规则集可能会从头再次运行。 如果其中一个规则导致redirect(无论是内部的还是外部的)导致请求过程重新开始,那么通常会发生这种情况。

所以[L]标志停止处理任何进一步的重写规则通过规则集。 但是,如果您的标记为[L]的规则修改了请求,并且您位于.htaccess上下文或<Directory>部分中,则您修改的请求将再次通过URLparsing引擎传回。 在下一个阶段,这次可能会有不同的规则。 如果你不明白发生了什么,看起来你的第一个重写规则与[L]标志没有任何作用。

最好的方法是使用[END]标志( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end )而不是[L]标志,如果你真的想停止所有进一步的规则处理(以及随后的重新分析)。 但是,[END]标志仅适用于Apache v2.3.9 +,因此如果v2.2或更低版本,则只能使用[L]标志。 在这种情况下,您必须依赖RewriteCond语句来防止在URLparsing引擎的后续传递中匹配规则。 或者你必须确保你的RewriteRule是在一个上下文(即httpd.conf),不会导致你的请求被重新分析。

另一个很棒的function是rewrite-map-expansions。 如果你有大量的主机/重写来处理,它们特别有用:

它们就像一个重要的价值替代品:

 RewriteMap examplemap txt:/path/to/file/map.txt 

然后你可以在你的规则中使用一个映射:

 RewriteRule ^/ex/(.*) ${examplemap:$1} 

有关这个主题的更多信息可以在这里find:

http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc

mod_rewrite可以在不改变URL的情况下修改请求处理的各个方面,例如设置环境variables,设置cookies等。这非常有用。

有条件地设置一个环境variables:

 RewriteCond %{HTTP_COOKIE} myCookie=(a|b) [NC] RewriteRule .* - [E=MY_ENV_VAR:%b] 

返回503响应: RewriteRule[R]标志可以取非3xx值并返回非redirect响应,例如,用于pipe理的停机/维护:

 RewriteRule .* - [R=503,L] 

将返回503响应(本身不是redirect )。

而且,mod_rewrite可以像mod_proxy的超级接口一样,所以你可以这样做,而不用写ProxyPass指令:

 RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L] 

观点:使用RewriteRuleRewriteCond将请求路由到不同的应用程序或负载均衡器,实际上可以根据请求的任何可能的方面进行传输,这是非常强大的。 控制到后端的请求,并能够在返回时修改响应,使mod_rewrite成为集中所有路由相关configuration的理想场所。

花时间学习它,这是非常值得的! 🙂