在Apache中重定向,更改URL或将HTTP重定向到HTTPS-您曾经想知道的有关Mod_Rewrite规则的所有内容,但害怕询问


264

这是关于Apache的mod_rewrite 的规范问题

使用mod_rewrite可以更改请求URL或将用户重定向到不同于他们最初请求的URL。这包括以下内容:

  • 将HTTP更改为HTTPS(或相反)
  • 将对不再存在的页面的请求更改为新的替换。
  • 修改URL格式(例如?id = 3433到/ id / 3433)
  • 根据月球和太阳下可能出现的一切,基于浏览器,引用者,不同页面显示不同的页面。
  • 您想弄乱网址的任何内容

您曾经想了解的有关Mod_Rewrite规则的所有内容,但都不敢问!

如何成为编写mod_rewrite规则的专家?

  • mod_rewrite规则的基本格式和结构是什么?
  • 我需要掌握哪些形式/风格的正则表达式?
  • 编写重写规则时最常见的错误/陷阱是什么?
  • 什么是测试和验证mod_rewrite规则的好方法?
  • 我应该注意mod_rewrite规则的SEO或性能影响吗?
  • 在常见情况下,mod_rewrite看起来像是适合该工作的正确工具,但不是吗?
  • 有哪些常见示例?

测试您的规则的地方

htaccess的测试网站是玩弄你的规则,并测试他们的好地方。它甚至显示调试输出,因此您可以查看匹配的内容和不匹配的内容。


9
这个问题背后的想法是为所有无尽的mod_rewrite问题提供一个封闭的路径,这些问题使我们更普通的用户发疯。这与在serverfault.com/questions/49765/how-does-subnetting-work上进行子网划分非常相似。
凯尔·布​​兰特

1
另外,我真的不希望在这个问题上有太多的赞成票,而是他们应该去回答。我不想进行此操作,因为我想确保张贴者对我希望通过mod_rewrite答案来结束所有mod_rewrite问题获得充分的肯定。
凯尔·布​​兰特

4
抱歉,我赞成这个问题。;-)我真的认为它需要显示在mod-rewrite标签搜索/过滤器顶部(或附近)。
史蒂文

其他人(tm)应该处理常见的用例。我对他们的了解还不够,无法做到公正。
sysadmin1138

也许应该将此问题链接到mod-rewrite标签wiki中,以使路径更短。
beldaz

Answers:


224

mod_rewrite语法顺序

mod_rewrite具有一些影响处理的特定排序规则。在完成任何操作之前,RewriteEngine On需要先给出指令,因为这会打开mod_rewrite处理。这应该在任何其他重写指令之前。

RewriteCond前面RewriteRule的条件使ONE规则受条件限制。后续的任何RewriteRules都将被视为没有条件的处理。

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule $/blog/(.*)\.html        $/blog/$1.sf.html

在这种简单情况下,如果HTTP引荐来源网址是来自serverfault.com,则将博客请求重定向到特殊的serverfault页面(我们就是这么特殊)。但是,如果上面的块有额外的RewriteRule行:

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule $/blog/(.*)\.html        $/blog/$1.sf.html
RewriteRule $/blog/(.*)\.jpg         $/blog/$1.sf.jpg

所有.jpg文件都将转到特殊的serverfault页面,而不仅仅是带有引荐来源网址的文件。显然,这不是这些规则的编写方式。可以使用多个RewriteCond规则来完成:

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.html        /blog/$1.sf.html
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.jpg         /blog/$1.sf.jpg

但是可能应该使用一些更棘手的替换语法来完成。

RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/$1.sf.$2

更复杂的RewriteRule包含要处理的条件。最后一个括号,(html|jpg)告诉RewriteRule匹配htmljpg,并在重写的字符串中将匹配的字符串表示为$ 2。从逻辑上讲,这与上一个块相同,只有两个RewriteCond / RewriteRule对,它只在两行而不是四行上执行。

多个RewriteCond行是隐式AND的,可以显式OR。要处理来自ServerFault和超级用户的引荐来源网址(显式OR):

RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)    [OR]
RewriteCond %{HTTP_REFERER}                ^https?://superuser\.com(/|$)
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/$1.sf.$2

要使用Chrome浏览器(隐含AND)投放ServerFault引用的页面,请执行以下操作:

RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)
RewriteCond %{HTTP_USER_AGENT}             ^Mozilla.*Chrome.*$
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/$1.sf.$2

RewriteBase也是特定于订单的,因为它指定以下RewriteRule指令如何处理它们。在.htaccess文件中,它非常有用。如果使用,它应该是.htaccess文件中“ RewriteEngine on”下的第一个指令。举个例子:

RewriteEngine On
RewriteBase /blog
RewriteCond %{HTTP_REFERER}           ^https?://serverfault\.com(/|$)
RewriteRule ^(.*)\.(html|jpg)         $1.sf.$2

这告诉mod_rewrite当前正在处理的此特定URL是通过http://example.com/blog/而不是物理目录路径(/ home / $ Username / public_html / blog)到达的,并相应地对其进行了处理。因此,用户RewriteRule认为它的字符串开始位于URL中的“ / blog”之后。这是用两种不同的方式写的同一件事。一个带有RewriteBase,另一个没有:

RewriteEngine On

##Example 1: No RewriteBase##
RewriteCond %{HTTP_REFERER}                                   ^https?://serverfault\.com(/|$)
RewriteRule /home/assdr/public_html/blog/(.*)\.(html|jpg)     $1.sf.$2

##Example 2: With RewriteBase##
RewriteBase /blog
RewriteCond %{HTTP_REFERER}           ^https?://serverfault\.com(/|$)
RewriteRule ^(.*)\.(html|jpg)         $1.sf.$2

如您所见,RewriteBase允许重写规则利用网站内容而不是Web 服务器的路径,这可以使编辑这些文件的人更容易理解它们。而且,它们可以使指令更短,从而具有美学吸引力。


RewriteRule匹配语法

RewriteRule本身具有用于匹配字符串的复杂语法。我将在另一部分介绍标志(诸如[PT]之类的东西)。因为系统管理员通过示例进行学习比通过阅读手册页进行学习的频率更高,所以我将给出示例并解释其工作方式。

RewriteRule ^/blog/(.*)$    /newblog/$1

.*构造匹配.零或多次(*)的任何单个字符()。将其括在圆括号中告诉它提供与$ 1变量匹配的字符串。

RewriteRule ^/blog/.*/(.*)$  /newblog/$1

在这种情况下,第一个。*不会括在括号中,因此不会提供给重写的字符串。此规则将删除新博客站点上的目录级别。(/blog/2009/sample.html变为/newblog/sample.html)。

RewriteRule ^/blog/(2008|2009)/(.*)$   /newblog/$2

在这种情况下,第一个括号表达式将设置一个匹配组。这将变成$ 1,这是不需要的,因此不会在重写的字符串中使用。

RewriteRule ^/blog/(2008|2009)/(.*)$   /newblog/$1/$2

在这种情况下,我们在重写的字符串中使用$ 1。

RewriteRule ^/blog/(20[0-9][0-9])/(.*)$   /newblog/$1/$2

该规则使用特殊的括号语法指定字符范围。[0-9]匹配数字0到9。此特定规则将处理2000年到2099年之间的年份。

RewriteRule ^/blog/(20[0-9]{2})/(.*)$  /newblog/$1/$2

这和以前的规则具有相同的作用,但是{2}部分告诉它两次匹配先前的字符(在这种情况下为方括号表达式)。

RewriteRule ^/blog/([0-9]{4})/([a-z]*)\.html   /newblog/$1/$2.shtml

这种情况下将匹配第二个匹配表达式中的任何小写字母,并尽可能匹配所有字符。该\.结构告诉它来治疗期间作为实际的时期,而不是特殊字符是在前面的例子。但是,如果文件名中包含破折号,它将中断。

RewriteRule ^/blog/([0-9]{4})/([-a-z]*)\.html  /newblog/$1/$2.shtml

这会在其中包含破折号的文件名被捕获。但是,正如方-括号表达式中的特殊字符一样,它必须是表达式中的第一个字符。

RewriteRule ^/blog/([0-9]{4})/([-0-9a-zA-Z]*)\.html   /newblog/$1/$2.shtml

此版本使用文件名中的字母,数字或-字符来捕获任何文件名。这是您在方括号表达式中指定多个字符集的方式。


RewriteRule标志

重写规则上的标志具有许多特殊含义和用例

RewriteRule ^/blog/([0-9]{4})/([-a-z]*).\html  /newblog/$1/$2.shtml  [L]

该标志[L]在上述表达式的末尾。可以使用多个标志,以逗号分隔。链接的文档描述了每个文档,但是无论如何它们都在这里:

L =最后。一旦匹配,则停止处理RewriteRules。订单数!
C =链。继续处理下一个RewriteRule。如果此规则不匹配,则不会执行下一个规则。稍后再详细介绍。
E =设置环境变量。Apache具有各种可能影响Web服务器行为的环境变量。
F =禁止。如果此规则匹配,则返回403-Forbidden错误。
G =消失。如果此规则匹配,则返回410-Gone错误。
H =处理程序。强制将请求视为指定的MIME类型进行处理。
N =下一步。强制规则重新开始并重新匹配。小心!可能会导致循环。
NC =无情况。允许jpg匹配jpg和JPG。
NE =无法逃脱。防止将特殊字符(。?#&等)重写为等效的十六进制代码。
NS =没有子请求。如果您使用的是服务器端包含文件,则将阻止与包含文件的匹配。
P =代理。强制该规则由mod_proxy处理。透明地从其他服务器提供内容,因为您的网络服务器会获取并重新提供内容。这是一个危险的标志,因为写得不好的标志会将您的Web服务器变成开放式代理,那就不好了。
PT =直通。在RewriteRule匹配中考虑Alias语句。
QSA = QSAppend。当原始字符串包含查询(http://example.com/thing?asp=foo)将原始查询字符串附加到重写后的字符串中。通常,它将被丢弃。对于动态内容很重要。
R =重定向。提供HTTP重定向到指定的URL。也可以提供确切的重定向代码[R = 303]。与极为相似RedirectMatch,后者速度更快,应尽可能使用。
S =跳过。跳过此规则。
T =类型。指定返回内容的MIME类型。与AddType指令非常相似。

您知道我怎么说RewriteCond仅适用于一条规则吗?好吧,您可以通过链接解决该问题。

RewriteEngine On
RewriteCond %{HTTP_REFERER}          ^https?://serverfault\.com(/|$)
RewriteRule ^/blog/(.*)\.html        /blog/$1.sf.html     [C]
RewriteRule ^/blog/(.*)\.jpg         /blog/$1.sf.jpg

因为第一个RewriteRule具有Chain标志,所以第二个rewrite-rule将在第一个rewriteRule匹配时执行,即与前一个RewriteCond规则匹配时执行。如果Apache正则表达式使您的大脑受伤,该功能非常方便。但是,从优化的角度来看,我在第一部分中指出的“一站式”方法更快。

RewriteRule ^/blog/([0-9]{4})/([-0-9a-zA-Z]*)\.html   /newblog/$1/$2.shtml

可以通过标志使它更简单:

RewriteRule ^/blog/([0-9]{4})/([-0-9a-z]*)\.html   /newblog/$1/$2.shtml   [NC]

另外,某些标志也适用于RewriteCond。值得注意的是,NoCase。

RewriteCond %{HTTP_REFERER}        ^https?://serverfault\.com(/|$)     [NC]

将匹配“ ServerFault.com”


9
做得好。[filler]
EEAA 2010年

3
非常好的mod_rewrite正则表达式底漆。+1。
史蒂文

3
它有时是有用知道,RewriteCond真正被处理RewriteRule匹配。您可能想在顶部附近说“稍后再说”,在上面说“ RewriteRule之前的RewriteCond使该一个规则服从条件”。您可能要提到,正则表达式是Perl兼容的正则表达式。此外,您在“ ... RewriteRule认为它是字符串开头...”中也有多余的撇号
Dennis Williamson 2010年

2
RewriteRule ^/blog/.*/(.*)$ /newblog/$1与第一个目录组件不匹配-默认情况下,重写器是贪婪的。/.*/(.*)匹配/ 1 /(2)/和/ 1/2/3/4/5 /(6)/,因此您需要/ [^ /] * /只匹配第一条路径零件。
适配器

1
@ sysadmin1138,我想这个答案是好的,但如果你更多地讨论标志E,N,NS,P,PT,和S结合实例,因为这些标志并不明显,他们是如何工作的它可以更好等等
Pacerier

39

mod_rewrite规则的基本格式和结构是什么?

在这些方面,我将遵循sysadmin1138的出色回答。

我需要掌握哪些形式/风格的正则表达式?

除了sysadmin1138概述的语法顺序,语法匹配/正则表达式和RewriteRule标志之外,我相信值得一提的是mod_rewrite公开基于HTTP请求标头和Apache的配置的Apache环境变量。

我建议使用AskApache的mod_rewrite调试教程,以获得可用于mod_rewrite的变量的完整列表。

编写重写规则时最常见的错误/陷阱是什么?

RewriteRule的大多数问题源于对PCRE语法的误解/未能正确转义特殊字符或对匹配变量的内容缺乏了解。

典型问题和建议的故障排除:

  • 500-内部服务器错误 - 删除配置文件中的Windows托架控件(如果存在),确保启用了mod_rewrite(在IfModule有条件的情况下包装指令以避免这种情况),检查指令语法,注释掉指令,直到发现问题
  • 重定向循环 -使用RewriteLog和RewriteLogLevel,注释掉指令直到发现问题

什么是测试和验证mod_rewrite规则的好方法?

首先,查看计划与之匹配的环境变量的内容-如果已安装PHP,这就像在应用程序中添加以下代码块一样简单:

<?php
  var_dump($_SERVER);
?>

...然后编写您的规则(最好在开发服务器上进行测试),并注意Apache ErrorLog文件中的任何不一致的匹配或活动。

对于更复杂的规则,请使用mod_rewrite RewriteLog指令将活动记录到文件中并进行设置RewriteLogLevel 3

我应该注意mod_rewrite规则的SEO或性能影响吗?

AllowOverride all这会影响服务器性能,因为Apache必须检查.htaccess文件并根据每个请求分析指令-如有可能,请将所有指令保留在站点的VirtualHost配置中,或者.htaccess仅对需要它们的目录启用替代。

Google的《网站站长指南》明确规定:“不要欺骗用户或向搜索引擎展示与向用户显示的内容不同的内容,这通常被称为“伪装”。”-避免创建可过滤搜索引擎机器人的mod_rewrite指令。

搜索引擎机器人喜欢一个1:1的内容:URI的映射(这是排名链接内容的基础) -如果你正在使用mod_rewrite创建临时重定向或您所服务在多个URI的内容相同,考虑指定一个标准URI内您的HTML文档。

在常见情况下,mod_rewrite看起来像是适合该工作的正确工具,但不是吗?

这本身就是一个巨大的(可能会引起争议的)主题-更好(IMHO)可以根据具体情况解决使用问题,并让提问者确定建议的解决方案是否适合他们的需求。

有哪些常见示例?

AskApache的mod_rewrite技巧和窍门几乎涵盖了经常弹出的每个常见用例,但是,给定用户的“正确”解决方案可能取决于用户配置和现有指令的复杂程度(这就是为什么它通常是最好在出现mod_rewrite问题时查看用户使用了哪些其他指令)。


感谢您的AskApache链接。这就是我想要的!
sica07

ASF正式不支持AskApache小丑。他所说的大部分内容值得商or或完全错误。
适配器

@adaptr请分享您显然知道的高级资源。
danlefree 2012年

“在常见情况下,mod_rewrite看起来像是适合该工作的工具,但不是吗?” - 简单的重定向,其中尚未使用mod_rewrite。使用mod_alias RedirectRedirectMatch代替。另请参见Apache文档:何时不使用mod_rewrite
MrWhite

21

像许多管理员/开发人员一样,多年来我一直在努力应对复杂的重写规则,并且对现有的Apache文档感到不满意,因此我决定作为一个个人项目,深入研究如何mod_rewrite实际工作以及如何与其余的Apache交互核心,所以在过去的几个月中,我一直在对strace+钻取源代码的工具进行测试,以获取所有相关信息。

以下是重写规则开发人员需要考虑的一些关键意见:

  • 重写的某些方面对于服务器配置,虚拟主机,目录,.htaccess处理是通用的,但是
  • 与PerDir(.htaccess)处理相反,根配置(服务器配置,虚拟主机和目录)的某些处理非常不同。
  • 更糟糕的是,由于PerDir处理几乎可以不加选择地触发INTERNAL REDIRECT循环,因此必须写出根配置元素,注意此类PerDir处理可以触发此操作。

我会这么说,因此,您几乎需要将重写用户社区分为两类,并将它们完全分开:

  • 具有root访问Apache配置的用户。这些通常是具有专用服务器/ VM的管理员/开发人员,这里的信息非常简单:.htaccess尽可能避免使用文件;在服务器或vhost配置中执行所有操作。调试非常容易,因为开发人员可以设置调试并可以访问rewrite.log文件。

  • 共享托管服务(SHS)的用户

    • 由于没有其他选择,因此此类用户必须使用.htaccess/ Perdir处理。
    • 更糟糕的是,此类用户的技能水平(就使用使用mod_rewrite的regexp驱动的梯形逻辑而言)通常远低于经验丰富的管理员。
    • Apache和托管服务提供商不提供调试/诊断支持。唯一的诊断信息是成功的重定向,即重定向到错误的URI。或404/500状态代码。这使他们感到困惑和无助。
    • Apache在解释此用例的重写方式方面非常虚弱。例如,它没有提供有关.htaccess选择哪个PerDir 文件及其原因的清晰说明。它没有解释PerDir自行车的复杂性以及如何避免这种情况。

可能有第三个社区:SHS提供程序中的管理和支持人员最终在两个营地都站了起来,不得不承受上述后果。

我已经写了几篇文章风格的博客文章(例如,有关在.htaccess文件中使用重写规则的更多信息),其中涵盖了许多详细的要点,在这里我不再赘述。我有自己的共享服务,并且支持一些专用的VM FLOSS项目。我最初使用标准的LAMP VM作为我的SHS帐户的测试工具,但最终我发现最好做一个合适的镜像VM(在此进行介绍)。

但是,就管理员社区应如何支持.htaccess用户而言,我认为我们需要发展并提供:

  • 有关重写系统在PerDir处理中实际工作方式的连贯描述
  • 一组有关如何编写.htaccess重写规则的准则/最佳实践
  • 一个简单的基于Web的重写脚本解析器,类似于W3C html解析器,但通过它用户可以输入测试URI或相同的测试向量,并立即获得重写逻辑流的日志/
  • 有关如何从规则中获取内置诊断的提示(例如

    • 使用[E=VAR:EXPR]利用EXPR将扩展后向引用($ N或%N)的事实,使它们可以用作目标脚本的诊断。
    • 如果您使用[OR],[C],[SKIP]和[L]标志局部地对重写规则进行排序,以便整个重写方案都可以工作无需利用内部重定向,则可以将以下内容添加为规则1,以避免所有循环麻烦:

      RewriteCond %{ENV:REDIRECT_STATUS} !=""
      RewriteRule .  -  [L]
      

这是有据可查的。为什么说文档没有解释这一点?
适配器

2
您所要做的就是订阅.htaccess主题,您将看到。大多数初学者无可避免地感到困惑-他们中的大多数人初次体验LAMP服务,并在共享服务上使用mod_rewrite,因此无法对系统/ vhost配置进行root访问,因此必须通过.htaccess文件使用每个目录进行处理。初学者必须“流血”一些重要区别。我将自己视为超级用户,并且仍在发现微妙之处。正如我所说的,我不得不使用strace和源代码扫描来解决某些方面的问题。:-(
TerryE

我完全同意。“我们需要将重写用户社区分为两类,并将它们完全分开。” 一些用户正在使用共享主机,甚至需要依靠.htaccess,它非常脆弱,复杂且令人困惑。我仍然有麻烦。
Ryan

15

使用重写图

您可以使用rewritemaps做很多事情。Rewritemaps使用Rewritemap指令声明,然后可以在RewritCond评估和RewriteRule替代中使用。

RewriteMap的常规语法为:

RewriteMap MapName MapType:MapSource

例如:

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

然后,您可以将mapname用于以下结构:

${examplemap:key}

该映射包含键/值对。如果找到密钥,则该值将被替换。简单映射只是纯文本文件,但是您可以使用哈希映射,甚至SQL查询。更多详细信息在文档中:

http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewritemap

字符串转义。

您可以使用四个内部映射进行一些操作。特别是转义字符串可以派上用场。

例如:我想测试查询字符串中的字符串“café”。但是,浏览器会在将其发送到我的服务器之前对其进行转义,因此,我需要弄清楚我希望匹配的每个字符串的URL转义版本是什么,或者我可以取消转义。

RewriteMap unescape int:unescape

RewriteCond %{QUERY_STRING}  (location|place)=(.*)
RewriteCond ${unescape:%2}   café
RewriteRule ^/find/$         /find/1234? [L,R]

请注意,我如何使用一个RewriteCond仅捕获查询字符串参数的参数,然后使用第二个rewriteCond中的映射对它进行转义。然后对此进行比较。另请注意,我需要我们%2作为rewritemap中的键,因为%1将包含“位置”或“位置”。当您使用括号对模式进行分组时,无论是否计划使用捕获的结果,它们都将被捕获...


最后一句话不太正确。该mod_rewrite正则表达式引擎支持非捕获基团,例如(?:location|place),这将只需要在例如一个捕获。
TerryE

12

编写重写规则时最常见的错误/陷阱是什么?

一个真正容易的陷阱是,当您重写更改外观路径的URL时,例如从 /base/1234/index.html/base/script.php?id=1234。客户端将找不到与脚本位置具有相对路径的任何图像或CSS。可以在此常见问题解答上找到许多解决方案。


1
感谢您的链接。尤其是在与不熟悉重写的其他团队成员一起工作时,我发现添加<base>标签最容易遵循并且仍启用相对路径。
kontur 2012年
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.