这是关于Apache的mod_rewrite的典型问题 。
更改请求URL或将用户redirect到与原始请求不同的URL是使用mod_rewrite完成的。 这包括诸如:
一切你想知道Mod_Rewrite规则,但不敢问!
我怎样才能成为写mod_rewrite规则的专家?
一个地方来testing你的规则
htaccesstesting仪网站是一个很好的地方玩你的规则和testing。 它甚至显示debugging输出,所以你可以看到什么匹配,什么没有。
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匹配html
或jpg
,并在重写的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本身具有用于匹配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议的故障排除
IfModule
包装指令,以避免此情况),检查指令语法,注释掉指令,直到发现问题 什么是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引擎呈现不同于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用例到源代码来处理所有这些。
.htaccess
)处理相反。 我会这么说,因为这个原因,你几乎需要将重写用户社区分成两类,并把它们看作是完全独立的:
那些拥有对Apacheconfiguration的root访问权限的用户 。 这些通常是具有应用程序专用服务器/虚拟机的pipe理员/开发人员,这里的信息非常简单:尽可能避免使用.htaccess
文件; 在你的服务器或虚拟主机configuration中执行一切。 由于开发人员可以设置debugging并可以访问rewrite.log文件,因此debugging非常简单。
共享托pipe服务(SHS)的用户 。
.htaccess
/ Perdir处理,因为没有其他select可用。 .htaccess
文件被选中的原因和原因。 这并不能解释PerDir循环的复杂性,以及如何避免这种情况。 可能还有第三个社区:SHS提供商的pipe理人员和支持人员最终在这两个阵营中站稳脚跟,不得不承受上述的后果。
我已经写了几篇文章式的博客文章(比如关于在.htaccess文件中使用Rewrite规则的更多内容 ),其中涵盖了很多细节点,在此不再赘述。 我有我自己的共享服务,并支持一些专用的VM FLOSS项目。 我开始使用一个标准的LAMP虚拟机作为我的SHS帐户的testing工具,但最终我发现最好做一个适当的镜像虚拟机( 在这里描述)。
但是,就pipe理界应该如何支持.htaccess
用户而言,我觉得我们需要开发和提供:
.htaccess
重写规则的一套准则/最佳实践 提示如何从规则中获取内置诊断(例如,
[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解决这个问题的一些选项。