这是关于Apache的mod_rewrite的规范性问题。这包括诸如以下内容:


将HTTP更改为HTTPS(或相反)
将请求更改为不再存在的页面以进行新的替换。
修改URL格式(例如?id = 3433到/ id / 3433)
基于浏览器,基于引荐来源,基于月亮和太阳的任何可能呈现不同的页面。
您想弄乱URL的任何内容

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


mod_rewrite规则的基本格式和结构是什么?
我需要扎实地理解正则表达式的哪种形式/风格?
什么?编写重写规则时最常见的错误/陷阱是什么?
什么是测试和验证mod_rewrite规则的好方法?
我应该意识到mod_rewrite规则的SEO或性能影响吗?
有普通坐吗
有哪些常见示例?

测试规则的地方

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

评论

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

另外,我真的不希望在这个问题上有太多的投票,相反,他们应该去回答。我不想继续这样做,因为我想确保张贴者对我希望通过mod_rewrite答案来结束所有mod_rewrite问题获得充分的评价。

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

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

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

#1 楼

mod_rewrite语法顺序

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

RewriteCond之前的RewriteRule使一个规则受条件限制。以下所有RewriteRules都将被视为不受条件约束。

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


在这种简单情况下,如果HTTP引用人来自serverfault.com,则重定向博客请求到特殊的serverfault页面(我们就是这么特殊)。但是,如果上面的代码块有多余的RewriteRule行:

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


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

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


,但可能应该使用一些更复杂的替换语法来完成。

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


更复杂的RewriteRule包含要处理的条件。最后一个括号(html|jpg)告诉RewriteRule匹配htmljpg,并在重写的字符串中将匹配的字符串表示为$ 2。从逻辑上讲,它与前一个块相同,只有两个RewriteCond / RewriteRule对,它只在两行而不是四行上执行。要同时处理ServerFault和超级用户的引荐来源网址(明示或):

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


要在Chrome浏览器中为ServerFault引荐的页面提供服务(隐式AND): />
RewriteEngine On
RewriteCond %{HTTP_REFERER}                ^https?://serverfault\.com(/|$)
RewriteCond %{HTTP_USER_AGENT}             ^Mozilla.*Chrome.*$
RewriteRule ^/blog/(.*)\.(html|jpg)        /blog/.sf.



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

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


这是告诉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)     .sf.

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


如您所见,RewriteBase允许重写规则利用网站的内容路径而不是Web服务器,这可以使他们对编辑此类文件的人更易懂。此外,它们还可以使指令更短,从而具有美观的外观。


RewriteRule匹配语法

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

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


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

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


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

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


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

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


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

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


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

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


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

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


此大小写将匹配任何小写字母在第二个匹配表达式中,并尽可能使用尽可能多的字符。 \.构造告诉它将周期视为实际周期,而不是前面示例中的特殊字符。但是,如果文件名中包含破折号,它将断开。

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


这会捕获其中包含破折号的文件名。但是,由于-是括号表达式中的特殊字符,因此它必须是表达式中的第一个字符。或文件名中的-字符。这是在括号表达式中指定多个字符集的方式。


RewriteRule标志

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

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


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

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

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

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


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

标志:

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


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

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


将匹配“ ServerFault.com”

评论


做得好。 [填料]

–EEAA
2010-12-20 19:20

非常好的mod_rewrite和regex入门。 +1。

–周一星期一
2010-12-20 23:24

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

–丹尼斯·威廉姆森
2010-12-20 23:57

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

–适配器
2012年4月12日12:55在

@ sysadmin1138,我认为这个答案很好,但是如果您通过示例详细说明标志E,N,NS,P,PT和S,则可能会更好,因为这些标志并不明显如何工作等。

–起搏器
2013年8月5日在2:17



#2 楼


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


我将在这些方面服从sysadmin1138的出色回答。


我需要扎实的
正则表达式的什么形式/风格?



除了语法顺序,语法匹配/正则表达式以及sysadmin1138概述的RewriteRule标志,我相信值得一提的是mod_rewrite会根据HTTP请求标头和Apache的配置公开Apache环境变量。可以用于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规则I
是否有SEO或性能含义?


AllowOverride all影响服务器性能,因为Apache必须检查.htaccess文件并使用每个请求分析指令-如果可能,请将所有指令保留在站点的VirtualHost配置中或启用.htaccess覆盖

Google的《网站管理员指南》明确指出:“不要欺骗用户或向搜索引擎展示与向用户显示的内容不同的内容,这通常被称为“隐藏” 。'“-避免创建用于过滤搜索引擎机器人的mod_rewrite指令。

搜索引擎机器人更喜欢1:1 content:URI映射(这是对内容链接进行排名的基础)-如果您使用mod_rewrite创建临时重定向或您正在提供服务在多个URI下具有相同的内容,请考虑在您的HTML文档中指定一个规范的URI。


是否有常见的情况,其中
mod_rewrite看起来像是正确的
工具,


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


常见示例有哪些?


问问Apache的mod_rewrite技巧和窍门仅涉及关于定期弹出的每个常见用例,但是,给定用户的“正确”解决方案可能取决于用户配置和现有指令的复杂程度(因此,通常最好查看其他哪些指令)每当出现mod_rewrite问题时,用户就位。)

评论


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

–sica07
11年11月23日在22:14

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

–适配器
2012年4月12日在12:59

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

– Danlefree
2012年4月13日,0:56

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

–怀特先生
16 Dec 8'在15:27

#3 楼

像许多管理员/开发人员一样,多年来我一直在努力应对复杂的重写规则,并且对现有的Apache文档不满意,因此我决定作为一个个人项目,深入研究mod_rewrite的实际工作原理并与之交互。 Apache核心,因此在过去的几个月中,我一直在使用strace +钻探源代码来测试测试用例,以获取所有这些信息的处理方法。需要考虑:


重写的某些方面对于服务器配置,虚拟主机,目录,.htaccess处理是通用的,但是某些处理对于根配置(服务器配置,虚拟主机和目录)与PerDir(.htaccess)处理相对。可以触发这个。
我会这么说,因此您几乎需要将重写用户社区分为两类,并将它们完全分开:


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


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



可能存在第三个社区:SHS提供者中的管理员和支持人员最终以在两个营地中站起来,都必须承受以上后果。

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

但是,在管理员社区应如何支持.htaccess用户方面,我认为我们需要开发并提供以下内容:重写系统实际上在PerDir处理中起作用。
关于如何编写.htaccess重写规则的一组准则/最佳实践
类似于W3C html解析器的一种基于Web的简单重写脚本解析器,但是用户可以输入相同的测试URI或测试向量,并立即获得重写逻辑流的日志/

关于如何从规则中获取内置诊断的提示(例如


利用[E=VAR:EXPR]充分利用了以下事实:EXPR将扩展反向引用($ N或%N),以使其可作为目标脚本的诊断程序。

如果使用[OR],[C],[SKIP]和[L]标志局部地对重写规则进行排序,以便整个重写方案都可以工作而无需利用内部重定向,则可以将以下内容添加为规则1,以避免所有循环麻烦:

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






评论


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

–适配器
2012年4月12日12:57

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

– TerryE
2012年4月13日在16:25

我完全同意。 “我们需要将重写用户社区分为两类,并将它们完全分开。”一些用户正在使用共享主机,并且需要依赖.htaccess,即使对于专家来说,.htaccess也是非常脆弱,复杂和混乱的。我仍然有麻烦。

–瑞安
17年7月19日在17:05

#4 楼

使用rewritemap

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作为重写映射中的键,因为%1将包含“位置”或“位置”。当您使用括号对模式进行分组时,无论是否计划使用捕获的结果,它们都将被捕获。

评论


最后一句话不太正确。 mod_rewrite正则表达式引擎支持非捕获组,例如(?:location | place),并且在示例中仅捕获一个。

– TerryE
17 Mar 10 '17 at 23:32

#5 楼


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


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

评论


感谢您的链接。尤其是在与不熟悉重写的其他团队成员一起工作时,我发现添加标签最容易遵循并且仍启用相对路径。

– kontur
2012年5月20日11:12