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
常见用例
-
将所有请求汇集到一个点上:
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
。 -
处理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]
-
强制SSL
RewriteEngine on RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://example.com/$1 [R,L]
-
常用标志:
-
[R]
或[redirect]
– 强制redirect(默认为302临时redirect) -
[R=301]
或[redirect=301]
– 强制301永久redirect -
[L]
或[last]
– 停止重写过程(请参阅以下常见陷阱注意事项) -
[NC]
或[nocase]
– 指定匹配应该不区分大小写
使用长forms的标志通常更具可读性,并且可以帮助其他人稍后阅读您的代码。
你可以用逗号分隔多个标志:
RewriteRule ^olddir(.*)$ /newdir$1 [L,NC]
-
常见的陷阱
-
混合
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_alias
与mod_rewrite
混合使用,但是它涉及到的工作多于上面处理基本redirect的工作。 -
上下文影响语法
在
.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
-
[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]
观点:使用RewriteRule
和RewriteCond
将请求路由到不同的应用程序或负载均衡器,实际上可以根据请求的任何可能的方面进行传输,这是非常强大的。 控制到后端的请求,并能够在返回时修改响应,使mod_rewrite成为集中所有路由相关configuration的理想场所。
花时间学习它,这是非常值得的! 🙂