mod_rewrite的隐藏功能


Answers:


203

哪里放置mod_rewrite规则

mod_rewrite规则可以放在httpd.conf文件内,也可以放在文件内.htaccess。如果您可以访问httpd.conf,则在此处放置规则将带来性能优势(因为规则只处理一次,而不是每次.htaccess调用文件时都如此)。

记录mod_rewrite请求

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

# 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/302重定向:

    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] 
    

    注意:外部重定向隐式为302重定向:

    # 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]-强制重定向(默认为302临时重定向)
    • [R=301][redirect=301]-强制执行301永久重定向
    • [L][last]-停止重写过程(请参见以下常见陷阱中的注释)
    • [NC][nocase]-指定匹配不区分大小写


    使用长形式的标志通常更易读,并且将帮助其他后来阅读您的代码的人。

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

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

常见的陷阱

  1. 混合mod_alias样式重定向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,但它不仅需要处理上述的基本重定向,还需要做更多的工作。

  2. 上下文会影响语法

    .htaccess文件内,RewriteRule模式中不使用斜杠:

    # 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>节中,那么修改后的请求将再次通过URL解析引擎传递回去。而在下一次通过时,这次可能会匹配不同的规则。如果您不理解这一点,通常看起来您的[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 docs)代替[L]标志。但是,该[END]标志仅适用于Apache v2.3.9 +,因此,如果您的v2.2或更低版本,则仅受该[L]标志的困扰。

    对于早期版本,您必须依赖RewriteCond语句来防止URL解析引擎的后续遍历上的规则匹配。

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

    或者,您必须确保您的RewriteRule位于httpd.conf不会导致重新解析您的请求的上下文中(即)。


10
杜德(Dude),现在是Mod重写上互联网上最好的文章。我讨厌那件事。我是一个lighttpd异端分子,因为我讨厌mod_rewrite。
肯特·弗雷德里克

3
到目前为止,这一直是我在mod_rewrite上找到的最有用的指南。仅仅了解RewriteLog可以解决许多问题,以至于我花了几天的时间来查找问题变成了几分钟。(我的意思是规则已经写好了,但我不知道为什么它们不起作用)
乔·钦

1岁的职位,但是对我来说,我在SO上发现的更有用的东西之一。
艾里克(Erik)2010年

3
[L]标志表示规则在当前处理中处于最后状态,因为它们是内部重定向,因此不会停止重写,因此您dirBdirC在下一个htaccess处理中应用该规则。单独RewriteRule ^(.*)$ index.php?query=$1将内部重定向的无限循环(实际上它是经过10次迭代终止)。-1是因为建议[L]不是last。这不是终止重写过程,而是最后一步
kbec 2012年

3
我认为这RewriteCond %{HTTPS} off是检查HTTPS连接的首选方法(在您的示例中,将非SSL流量强制为HTTPS)
Madbreaks 2013年

22

如果您需要“阻止”内部重定向/重写以防止在.htaccess文件中发生,请查看

RewriteCond %{ENV:REDIRECT_STATUS} ^$

条件,如此处所述


谢谢,这只是解决了我的问题!
马修

也感谢我,拯救生命!
本杰明·

这确实是救生员!人们应该更加意识到这一点。实际上,我将在来到这里之前阅读过的.*带有[L]标志的每个问题中建议这样做。
Qwerty

我见过几个修改本200!=200^.^$。显然,将变量设置200为重定向,但其他页面(错误和填充)也将其设置为某个值。现在,这意味着你要么检查,如果它is emptyis not emptyis 200或者is not 200,这取决于你所需要的。
Qwerty 2014年

18

与RewriteBase的处理:

您几乎总是需要设置RewriteBase。如果您不这样做,则apache会猜测您的基础是目录的物理磁盘路径。因此,从此开始:

RewriteBase /

啊。这完全解决了我遇到的问题。感谢那!
汤姆·萨维奇

3
有什么说法RewriteBase .,或者说它应该保持URL不变,只是更改您指定的内容?
杰伊·K

谢谢,这是无价的信息。:)
AturSams,2011年

2
RewriteBaseRewriteRule指令中使用相对路径替换时才需要设置。最好避免使用相对路径。
MrWhite 2015年

2
我不同意这个答案。在我们的开发团队中,我们RewriteBase完全避免使用,因为几乎所有开发人员都误解了它的作用。正如@ w3d所说,仅当您要保存字符并希望将同一基数应用于一个文件中的所有RewriteRules时才需要它。如果您避免,您的代码对其他人可能会更清晰。
西蒙·伊斯特

13

其他陷阱:

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

Options -MultiViews

我不太了解MultiViews的所有功能,但是我知道它在活动时会弄乱我的mod_rewrite规则,因为它的属性之一是尝试“猜测”它认为自己在寻找的文件扩展名。

我将解释:假设您的Web目录中有2个php文件,分别为file1.php和file2.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知道的一切。然后,您开始尝试使用规则来尝试理解这种新情况背后的逻辑,但是您测试的次数越多,其意义就越小。

好的,总之,如果您希望mod_rewrite以近似逻辑的方式工作,则关闭MultiViews是朝着正确方向迈出的一步。

2-启用FollowSymlinks

Options +FollowSymLinks 

那一个,我真的不知道细节,但是我已经看过很多次了,所以就去做。


谢谢:)我注意到意外的意外惊喜,例如/ log / activity变成/log.txt/activity ..谢谢你的提示:) ..太糟糕的计算机永远不会使有趣的事情发生意外的事情,例如不小心在Facebook上引诱了所有女性同事:)
AturSams,2011年

1
+FollowSymLinksmod_rewrite出于模糊的安全原因,在文档中提到该功能对于所有工作都是必不可少的。
乔伊,

这里有两句话让我非常担心:“我对MultiViews的所有功能都不熟悉,但是我知道当激活时,它会弄乱我的mod_rewrite规则”,而这“那一点,我真的不知道,但是我已经看过很多次了,所以就去做吧。” 我希望像您这样的人不会对不确定的事情写出答案。
TheCarver 2013年

1
@PaparazzoKid:我认为您误以为是百科全书。这是一个由一群人组成的社区,他们对自己正在使用的技术有了更好的了解。与您之前的AW White和Joey不同,您的评论几乎没有价值。MV和FSL是许多Apache选项中的2个。我的答案是专门使用mod_rw时的陷阱,它是一个单独的模块,与某些选项冲突并且与其他选项兼容。我解释了MV如何影响mod_rw,并提到+ FSL是流行的建议。Joey确认这实际上是强制性的。你带什么到桌上?
Michael Ekoka 2013年

谢谢。我只是花了一个小时的最佳时间来使旧站点工作并尝试调试重写规则,却发现MultiViews覆盖了所有内容。
Andrew McCombe 2014年

5

公式可以通过以下示例完成:

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]

动态负载平衡:

如果使用mod_proxy平衡系统,则可以添加动态范围的工作服务器。

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

4

对[L]标志有更好的理解。[L]标志最后一个,您只需要了解是什么会导致您的请求再次通过URL解析引擎进行路由。从文档(http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l)(重点是我的):

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

如果要在.htaccess文件或<Directory>各节中使用RewriteRule,则一定要对规则的处理方式有所了解。简化的形式是,一旦处理了规则,重写的请求就会交还给 URL解析引擎,以对其进行处理。在处理重写的请求时,<Directory> 可能会再次遇到.htaccess文件或节,因此规则集可能会从头开始再次运行。最常见的情况是,如果其中一个规则导致重定向(内部或外部)导致请求过程重新开始,则将发生这种情况。

因此,[L]标志确实停止处理通过规则集传递的任何其他重写规则。但是,如果标有[L]的规则修改了该请求,并且您位于.htaccess上下文或该<Directory>节中,则修改后的请求将再次通过URL解析引擎传递回去。而在下一次通过时,这次可能会匹配不同的规则。如果您不了解所发生的情况,则看起来带有[L]标志的第一个重写规则无效。

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


3

另一个强大的功能是rewrite-map-expansions。如果您要处理大量的主机/重写,它们特别有用:

它们就像一个键值替换:

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

然后,您可以在规则中使用映射,例如:

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

有关此主题的更多信息,请参见:

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


如果使用.htaccess基于-的重写,请忽略此功能。在这种情况下不起作用。
TerryE 2012年

2
RewriteMap指令必须在服务器上下文(httpd.conf)中使用,但是一旦在服务器上下文中定义,就可以通过.htaccess文件中的RewriteRule使用该映射。
JaredC

2

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

有条件地设置环境变量:

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

返回503响应: RewriteRule[R]标志可以采用非3xx的值,并返回非重定向响应,例如用于管理的停机时间/维护:

RewriteRule .* - [R=503,L]

将返回503响应(本身不是重定向)。

另外,mod_rewrite可以充当mod_proxy的超级接口,因此您可以执行此操作,而不用编写ProxyPass指令:

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

意见:使用RewriteRules和RewriteConds根据请求的几乎任何可能的方面将请求路由到不同的应用程序或负载均衡器,功能非常强大。控制返回后端的请求,并能够修改返回的响应,使mod_rewrite成为集中所有与路由相关的配置的理想场所。

花时间学习它,这是值得的!:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.