在Apacheredirect,更改URL或redirectHTTP到HTTPS – 你想知道的关于Mod_Rewrite规则的一切,但不敢问

这是关于Apache的mod_rewrite的典型问题 。

更改请求URL或将用户redirect到与原始请求不同的URL是使用mod_rewrite完成的。 这包括诸如:

  • 将HTTP更改为HTTPS(或其他方式)
  • 将请求更改为不存在的页面以replace新的replace。
  • 修改URL格式(例如?id = 3433到/ id / 3433)
  • 基于引用者基于浏览器呈现基于月球和太阳下任何可能的任何事物的不同页面。
  • 任何你想搞乱的URL

一切你想知道Mod_Rewrite规则,但不敢问!

我怎样才能成为写mod_rewrite规则的专家?

  • mod_rewrite规则的基本格式和结构是什么?
  • 我需要掌握哪些正则expression式的forms/风格?
  • 编写重写规则时最常见的错误/陷阱是什么?
  • 什么是testing和validationmod_rewrite规则的好方法?
  • 是否有我应该知道的mod_rewrite规则的SEO或性能影响?
  • 有没有常见的情况下,mod_rewrite似乎是正确的工具,但不是?
  • 什么是一些常见的例子?

一个地方来testing你的规则

htaccesstesting仪网站是玩游戏规则和testing的好地方。 它甚至显示debugging输出,所以你可以看到什么匹配,什么没有。

mod_rewrite语法顺序

mod_rewrite有一些特定的sorting规则,影响处理。 在任何事情完成之前,需要给出RewriteEngine On指令,因为这将启用mod_rewrite处理。 这应该在任何其他重写指令之前。

RewriteCond先前的RewriteRule使得这个ONE规则受制于条件。 任何下面的重写规则将被处理,就好像它们不受条件限制。

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

在这种简单情况下,如果HTTP引用来自serverfault.com,则将博客请求redirect到特殊的服务器默认页面(我们只是特别的)。 但是,如果上面的块有一个额外的RewriteRule行:

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

所有的.jpg文件都会转到特殊的服务器默认页面,而不仅仅是那些带有引用的表示来自这里的页面。 这显然不是这些规则如何写的意图。 这可以用多个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 

但可能应该用一些更复杂的replace语法来完成。

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

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

多个RewriteCond行是隐含的与,并可以显式或。 处理来自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浏览器提供ServerFault引用页面(隐式AND):

 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/而不是物理目录path(/ home / $ Username / public_html / blog),并相应地处理它。 因此, RewriteRule认为它的string开始位于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允许重写规则利用网站的内容path而不是Web 服务器 ,这可以使编辑这些文件的人更容易理解。 而且,他们可以使指令更短,具有审美吸引力。


RewriteRule匹配语法

RewriteRule本身具有用于匹配string的复杂语法。 我将在另一节中介绍标志(比如[PT])。 由于系统pipe理员比阅读手册更经常学习,所以我会举例说明他们的工作。

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

.*构造匹配任何单个字符( . )零次或多次( * )。 将它括在圆括号中告诉它提供匹配的string作为$ 1variables。

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

在这种情况下,第一个*不包含在parens中,因此不会被提供给重写的string。 此规则将删除新博客网站上的目录级别。 (/blog/2009/sample.html变成/newblog/sample.html)。

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

在这种情况下,第一个括号expression式将build立一个匹配组。 这变成$ 1,这是不需要的,因此不会在重写的string中使用。

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

在这种情况下,我们在重写的string中使用$ 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}部分告诉它匹配前一个字符(在这个例子中是括号expression式)两次。

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

这种情况下,将匹配第二个匹配expression式中的任何小写字母,并尽可能多的字符。 \. 构造告诉它把这个时期当作一个实际时期,而不是前面例子中的特殊性质。 但是,如果文件名中有破折号,它将会中断。

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

这陷阱文件名称中有破折号。 但是,作为括号expression式中的特殊字符,它必须是expression式中的第一个字符。

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

这个版本用文字,数字或文件名中的-字符来捕捉任何文件名。 这是如何在括号expression式中指定多个字符集。


重写规则标志

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

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

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

L =最后。 一旦匹配,就停止处理RewriteRules。 订单数!
C =链。 继续处理下一个RewriteRule。 如果这个规则不匹配,那么下一个规则将不会被执行。 稍后再说。
E =设置环境variables。 Apache有各种可能影响networking服务器行为的环境variables。
F =禁止。 如果此规则匹配,则返回403-Forbidden错误。
G =走了。 如果此规则匹配,则返回410-Gone错误。
H =处理程序。 强制将请求处理为指定的MIMEtypes。
N =下一个。 强制规则重新开始并重新匹配。 小心! 可能会导致循环。
NC =没有情况。 允许jpg匹配JPG和JPG。
NE =没有逃跑。 防止将特殊字符(。?#&etc)重写为它们的hex等价forms。
NS =没有子请求。 如果你正在使用服务器端包含,这将阻止与包含的文件相匹配。
P =代理。 强制规则由mod_proxy处理。 透明地提供来自其他服务器的内容,因为您的Web服务器提取内容并重新提供服务。 这是一个危险的标志,因为写得不好的会把你的networking服务器变成一个开放的代理服务器,这是不好的。
PT =通过。 考虑RewriteRule匹配中的别名语句。
QSA = QSA出现。 当原始string包含查询( http://example.com/thing?asp=foo )时,将原始查询string追加到重写的string中。 通常它会被丢弃。 对于dynamic内容很重要。
R =redirect。 提供一个HTTPredirect到指定的URL。 也可以提供精确的redirect代码[R = 303]。 非常类似于RedirectMatch ,这是更快,应尽可能使用。
S =跳过。 跳过这条规则。
T =types。 指定返回内容的MIMEtypes。 非常类似于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有链标志,所以第二个重写规则会在第一个重写规则执行的时候执行,这是前一个RewriteCond规则匹配的时候。 如果Apache正则expression式使你的大脑受到伤害,方便 但是,从优化的angular度来看,我在第一部分中指出的一行一行的方法更快。

 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”

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

关于这些问题,我会推迟到sysadmin1138的出色答案。

我需要掌握哪些正则expression式的forms/风格?

除了语法顺序,语法匹配/正则expression式和由sysadmin1138概述的RewriteRule标志外,我还认为它提到了mod_rewrite公开了基于HTTP请求头和Apacheconfiguration的Apache环境variables。

我会推荐AskApache的mod_rewritedebugging教程 ,以获取mod_rewrite可用的全部variables列表。

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

RewriteRule的大部分问题都是由于对PCRE语法的误解,未能正确地转义特殊字符或缺乏对匹配所用variables内容的洞察。

典型问题和build议的故障排除

  • 500 – 内部服务器错误 – 删除configuration文件中的Windows托架控件 (如果存在),确保启用了mod_rewrite(为避免此情况,在IfModule包装指令),检查指令语法,注释掉指令,直到发现问题
  • redirect循环 – 使用RewriteLog和RewriteLogLevel,注释指令直到问题被识别

什么是testing和validationmod_rewrite规则的好方法?

首先,查看您计划匹配的环境variables的内容 – 如果安装了PHP,这就如同将以下代码块添加到应用程序一样简单:

 <?php var_dump($_SERVER); ?> 

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

对于更复杂的规则,使用mod_rewrite的RewriteLog指令将活动logging到文件并设置RewriteLogLevel 3

是否有我应该知道的mod_rewrite规则的SEO或性能影响?

AllowOverride all影响服务器的性能,因为Apache必须检查.htaccess文件并为每个请求分析指令 – 如果可能的话,为您的站点保留VirtualHostconfiguration中的所有指令或启用.htaccess仅覆盖需要它们的目录。

Google的网站pipe理员指南明确规定:“不要欺骗用户或向search引擎展示不同于内容的内容,这通常被称为”隐藏“。 – 避免创build针对search引擎机器人的mod_rewrite指令。

search引擎机器人更喜欢1:1内容:URI映射(这是排名链接到内容的基础) – 如果您使用mod_rewrite创build临时redirect,或者您在多个URI下提供相同内容,请考虑在内部指定规范URI你的HTML文件。

有没有常见的情况下,mod_rewrite似乎是正确的工具,但不是?

这本身就是一个巨大的(可能是有争议的)话题 – 更好地(IMHO)在个案的基础上解决使用问题,让被问话者决定所提出的决议是否适合他们的需要。

什么是一些常见的例子?

AskApache的mod_rewrite技巧和技巧涵盖了几乎所有常见的用例,但是,给定用户的“正确”解决scheme可能取决于用户configuration和现有指令的复杂性(这就是为什么它通常是好主意,看看有什么其他指令的用户有一个mod_rewrite问题出现的地方)。

像许多pipe理员/开发人员一样,我一直在反复重写规则多年,对现有的Apache文档感到不满,所以我决定作为一个个人项目来mod_rewrite了解mod_rewrite实际工作方式,并与其余的Apache核心,所以在过去的几个月里,我一直在用strace +钻取testing用例到源代码来处理所有这些。

以下是重写规则开发者需要考虑的一些关键评论:

  • 然而 ,重写的一些方面对于服务器configuration,虚拟主机,目录,.htaccess处理是很常见的
  • 对于根configuration(服务器configuration,虚拟主机和目录),与PerDir( .htaccess )处理相反,一些处理是非常不同的。
  • 更糟糕的是,因为PerDir处理几乎可以无差别地触发INTERNAL REDIRECT循环,所以必须写入根configuration元素,意识到这样的PerDir处理可以触发这个。

我会这么说,因为这个原因,你几乎需要将重写用户社区分成两类,并把它们看作是完全独立的:

  • 那些拥有对Apacheconfiguration的root权限的人 。 这些通常是具有应用程序专用服务器/虚拟机的pipe理员/开发人员,这里的信息非常简单:尽可能避免使用.htaccess文件; 在你的服务器或虚拟主机configuration中执行一切。 由于开发人员可以设置debugging并可以访问rewrite.log文件,因此debugging非常简单。

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

    • 这样的用户必须使用.htaccess / Perdir处理,因为没有其他select可用。
    • 更糟糕的是,这些用户的技能水平(使用mod_rewrite的正则expression式驱动的梯形逻辑)通常远远低于有经验的pipe理员。
    • Apache和主机提供商不提供debugging/诊断支持。 唯一的诊断信息是成功的redirect,redirect到错误的URI。 或者一个404/500状态码。 这让他们感到困惑和无奈。
    • Apache非常薄弱的​​解释了这个用例的重写是如何工作的。 例如,它没有提供PerDir .htaccess文件被选中的原因和原因。 这并不能解释PerDir循环的复杂性,以及如何避免这种情况。

可能还有第三个社区:SHS提供商的pipe理人员和支持人员最终在这两个阵营中站稳脚跟,不得不承受上述的后果。

我已经写了几篇文章式的博客文章(比如关于在.htaccess文件中使用Rewrite规则的更多内容 ),其中涵盖了很多细节点,在此不再赘述。 我有我自己的共享服务,并支持一些专用的VM FLOSS项目。 我开始使用标准的LAMP虚拟机作为我的SHS帐户的testing工具,但最终我发现最好是做一个适当的镜像虚拟机( 在这里描述)。

但是,就pipe理界应该如何支持.htaccess用户而言,我觉得我们需要开发和提供:

  • 关于重写系统如何在PerDir处理中实际工作的一致描述
  • 关于如何编写.htaccess重写规则的一套准则/最佳实践
  • 一个简单的基于web的重写脚本parsing器类似于W3C的htmlparsing器,但用户可以通过它input相同的testingURI或testing向量,并立即获得重写逻辑stream/
  • 提示如何从规则中获取内置诊断(例如,

    • 使用[E=VAR:EXPR]利用EXPR扩展反向引用($ N或%N)的事实,使它们可用作目标脚本的诊断。
    • 如果您使用[OR],[C],[SKIP]和[L]标志局部排列重写规则,以便整个重写scheme无需利用内部redirect就可以工作,则可以添加以下规则以避免所有循环的麻烦:

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

使用rewritemap

rewritemaps可以做很多事情。 Rewritemaps使用Rewritemap指令获取声明,然后可以在RewritCond评估和RewriteRule注入中使用。

RewriteMap的一般语法是:

 RewriteMap MapName MapType:MapSource 

例如:

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

然后你可以使用mapname来构造像这样的东西:

 ${examplemap:key} 

该地图包含键/值对。 如果find该键,则该值被取消。 简单的地图只是纯文本文件,但您可以使用哈希映射,甚至SQL查询。 更多细节在文档中:

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

翻转的string。

有四个内部地图可以用来做一些操作。 尤其擅长的string可以派上用场。

例如:我想在查询string中testingstring“café”。 但是,浏览器将它发送到我的服务器之前,所以我需要弄清楚什么URL逃跑版本是每个string我想匹配,或者我可以忽略它…

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

请注意,我如何使用一个RewriteCond来捕获参数toe查询string参数,然后使用第二个rewriteCond中的地图来避开它。 然后进行比较。 另外请注意我需要我们%2作为rewritemap中的关键字,因为%1将包含“位置”或“地点”。 当你使用圆括号分组模式,他们也将被捕获,你打算使用捕获的结果或不…

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

一个非常容易的错误是,当你重写改变明显path的URL时,例如从/base/1234/index.html/base/script.php?id=1234 。 任何图像或CSS与脚本位置的相对path将不会被客户端find。 可以在这个常见问题find解决这个问题的一些选项。