119

最近似乎有相当数量的mod_rewrite线程在浮动,对于它的某些方面如何工作有些困惑。因此,我编写了一些关于常见功能的注释,也许还有一些恼人的细微差别。

您在使用时遇到过哪些其他功能/常见问题mod_rewrite

4

8 回答 8

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 在该传递中被修改并且您在上下文或部分中,那么您修改的请求将再次通过 URL 解析引擎传递回。并且在下一次通过时,它可能会匹配不同的规则。如果您不理解这一点,通常看起来您的标志无效。.htaccess<Directory>[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语句来防止在 URL 解析引擎的后续传递中匹配规则。

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

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

于 2008-11-13T01:15:42.970 回答
22

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

RewriteCond %{ENV:REDIRECT_STATUS} ^$

条件,如此处所述

于 2010-04-22T06:05:22.997 回答
18

与 RewriteBase 的交易:

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

RewriteBase /
于 2009-08-27T03:07:26.327 回答
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 

那个,具体细节我也不是很清楚,但是看过很多次,就照着做吧。

于 2009-08-19T10:08:10.930 回答
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]
于 2010-01-19T21:42:26.580 回答
4

为了更好地理解 [L] 标志。[L] 标志最后一个,您只需要了解导致您的请求再次通过 URL 解析引擎路由的原因。从文档(http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l)(强调我的):

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

<Directory>如果您在 .htaccess 文件或section中使用 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)。

于 2014-11-14T12:25:23.070 回答
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

于 2009-08-19T10:18:10.787 回答
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 成为集中所有与路由相关的配置的理想场所。

花点时间学习一下,非常值得!:)

于 2014-02-12T16:22:11.273 回答