【问题标题】:Hidden features of mod_rewritemod_rewrite 的隐藏特性
【发布时间】:2010-09-22 02:37:08
【问题描述】:

最近似乎有相当数量的mod_rewrite 线程在四处游荡,对它的某些方面如何工作感到有些困惑。因此,我整理了一些关于常见功能的注释,也许还有一些令人讨厌的细微差别。

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

【问题讨论】:

标签: apache .htaccess mod-rewrite


【解决方案1】:

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_aliasmod_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] 标志 (see 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)。

【讨论】:

  • 老兄,现在完全是互联网上最好的关于 mod rewrite 的文章。我讨厌那个东西。我是一个 lighttpd 异端因为我多么讨厌 mod_rewrite。
  • 这是迄今为止我在 mod_rewrite 上找到的最有用的指南。仅仅发现 RewriteLog 就帮助解决了很多问题,以至于我花了几天时间才找到的东西变成了几分钟。 (我的意思是规则已经写好了,但我不知道为什么它们不起作用)
  • 1 年前的帖子,但我在 SO 上发现的更有用的东西之一 - 对我来说。
  • [L] 标志表示规则在当前处理中是 last,这不会停止重写,因为它们是内部重定向,所以您的 dirB 适用于 @987654367 @ 在下一个 htaccess 处理中。单独RewriteRule ^(.*)$ index.php?query=$1 将是内部重定向的无限循环(实际上它在 10 次迭代后终止)。 -1 因为你建议 [L] 不是最后一个。这不是终止重写过程,而是这是最后一个
  • 我相信 RewriteCond %{HTTPS} off 是检查 HTTPS 连接的首选方式(在您的示例中,强制非 SSL 流量到 HTTPS)
【解决方案2】:

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

RewriteCond %{ENV:REDIRECT_STATUS} ^$

条件,如discussed here

【讨论】:

  • 谢谢,这正好解决了我的问题!
  • 也感谢我,救命!
  • 这真是救命啊!人们应该更加意识到这一点。事实上,我会向我在到达这里之前阅读的带有[L] 标志的每个关于.* 的问题提出这个建议。
  • 我已经看到对这个200!=200^.^$ 的一些修改。显然,该变量被设置为200 以进行重定向,但其他页面(错误和其他内容)也将其设置为某个值。现在这意味着您可以根据需要检查它是is emptyis not emptyis 200 还是is not 200
【解决方案3】:

与 RewriteBase 的交易:

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

RewriteBase /

【讨论】:

  • 啊。这完全解决了我遇到的问题。谢谢!
  • 有什么说法RewriteBase .,或者说它应该保持URL不变,只是改变你指定的内容?
  • 谢谢,这是一个无价的信息。 :)
  • 如果您在RewriteRule 指令中使用相对路径替换,您只需要设置RewriteBase。最好避免使用相对路径。
  • 我不同意这个答案。在我们的开发团队中,我们完全避免使用RewriteBase,因为几乎所有开发人员都误解了它的作用。正如@w3d 所说,只有当您想要保存字符并且想要将相同的基础应用于一个文件中的所有 RewriteRules 时,您才需要它。如果您避免使用代码,其他人可能会更清楚。
【解决方案4】:

其他陷阱:

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 .. 感谢您的提示 :) .. 太糟糕的计算机永远不会取笑意外的事情发生,例如意外引诱你所有脸书上的女同事 :)
  • +FollowSymLinks 在文档中被提及是mod_rewrite 必须工作的必要条件,出于模糊的安全原因。
  • 这里有两个陈述让我非常担心:'我对 MultiViews 的所有功能都不太了解,但我知道它在激活时会扰乱我的 mod_rewrite 规则'和这个'那个,我不知道细节,但我已经看到很多次提到它,所以就去做吧。我希望像你这样的人不要在 SO 上写关于你不确定的事情的答案。
  • @PaparazzoKid:我认为您将 SO 误认为是百科全书。这是一个人们聚集在一起以了解他们正在使用的技术的社区。不像 A.W.怀特和乔伊在你面前,你的评论几乎没有价值。 MV 和 FSL 是 Apache 的众多选项中的两个。我的回答是关于使用 mod_rw 时的陷阱,特别是一个单独的模块,它与某些选项冲突并与其他选项一起使用。我解释了 MV 如何影响 mod_rw 并提到 +FSL 是一个受欢迎的推荐。乔伊证实这实际上是强制性的。你带来了什么?
  • 谢谢。我刚刚花了一个小时的大部分时间让一个遗留站点工作并尝试调试重写规则,却发现 MultiViews 覆盖了这一切。
【解决方案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]

【讨论】:

    【解决方案6】:

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

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

    如果您在 .htaccess 文件或 &lt;Directory&gt; 部分中使用 RewriteRule,了解以下内容很重要 如何处理规则。这个的简化形式是,一旦 规则已处理完毕,将重写的请求返回 URL 解析引擎用它来做它可能做的事情。它可能是 在处理重写请求时,.htaccess 文件或&lt;Directory&gt; 部分可能会再次遇到,因此可以运行规则集 再次从头开始。最常见的情况是,如果其中一个 规则导致重定向 - 无论是内部的还是外部的 - 导致 请求流程重新开始。

    因此 [L] 标志 停止处理 通过 规则的任何进一步重写规则放。但是,如果您的带有 [L] 标记的规则修改了请求,并且您位于 .htaccess 上下文或 &lt;Directory&gt; 部分,那么您修改后的请求将再次通过 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)中。

    【讨论】:

      【解决方案7】:

      另一个很棒的功能是 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 的重写,请忽略此功能。在这种情况下它不起作用。
      • RewriteMap 指令必须在服务器上下文 (httpd.conf) 中使用,但一旦在那里定义,您可以通过 .htaccess 文件中的 RewriteRule 使用映射。
      【解决方案8】:

      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 成为集中所有与路由相关的配置的理想场所。

      花时间学习它,非常值得! :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-19
        • 1970-01-01
        • 1970-01-01
        • 2010-11-07
        • 2010-09-15
        • 2010-11-07
        • 2010-10-31
        • 2011-01-30
        相关资源
        最近更新 更多