http://article.yeeyan.org/view/jcky/59298
深入理解Apache的mod_rewrite jcky于2009-09-24 13:17:38翻译 | 已有7765人浏览 | 有0人评论
人们一提到.htaccess配置文件,就会意识到它的强大功能及复杂性。本文作者试图循序渐进的给我们介绍mod_rewrite,使人们以后不在.htaccess面前望而却步。
Tags:linux | apache | mod_rewrite
人们一提到.htaccess配置文件,首先映入他们脑海的就是用mod_rewrite进行URL地址重定向。对mod_rewrite的看法各不相同,为了就人们对mod_rewrite是怎么认识的有一个快速的看法,我在twitter上搜索了一下"mod_rewrite",并且将我写这篇文章时的前几个搜索页面的结果找出来:
midk:啊!.hatccess和mod_rewrite是如此的痛苦……
basterzenbach:我喜欢mod_rewrite。在我的有生之年,我都可以用它工作,并且还是不能精通它――太强大了。
mikemackay:仍然喜欢mod_rewrite的灵活性――又得到了拯救。这往往容易被忽略……并且要比你想想的要简单!
hostpc:我讨厌mod_rewrite。无法用它正常工作。
awanderingmind:噢,Wordpress 和Apache,你们带给了我烦恼。该死的mod_rewrite!
danielishiding:为什么mod_rewrite不工作了!该死!
我注意到人们清楚的认识到了mod_rewrite的强大,但是往往在语法面前望而却步。考虑到Apache的mod_rewrite文档在前面几页说了同样的问题,这并不奇怪:
“mod_rewrite例子和文档的数量,尽管可以以吨来计算,但是它是巫术。该死的冷漠的巫术,但仍然是巫术。“――-布莱恩摩尔
太糟糕了!因此,在本文中。我真的试图使mod_rewrite的难度降低一个档次。我不仅要去尝试解决mod_rewrite的的语法,还要设法提供一个工作流程,使你可以通过它调试和解决你的mod_rewrite问题。我也会给你一些有用的现实世界中的例子。
然 而,在开始之前,我还要做一个警告。许多学科,尤其是这个,除非你自己动手尝试,否则你是不会学会的!这就是为何我会更专注于教授一个调试工作流程。像往常一样,如果你还没有加载模块,我会告诉你如何安装好你的系统。我敦促你们在你们自己服务器上做这些例子,如果是测试环境,则更好。你的经验和成功次数越多,你就会越容易将这种知识扩展到更高级的例子和应用。享受吧。
mod_rewrite的是什么?
mod_rewrite的是一个Apache 模块,可使服务器操纵请求的网址。根据一系列规则对传入的网址进行检查,规则中包含一个正则表达式来检测特定的格式。 如果在地址中发现了一个格式,并且满足适当的条件,该格式就会被一个替代的字符串或者是动作取代。这一过程一直在进行着,直到没有更多的规则或是程序被明确告诉停止。
上面的内容可以总结为以下3点:
*有一个按顺序排列的处理规则列表。
*如果有一个规则相匹配,它会检查那条规则满足的条件。
*如果一切都匹配,它会替代或这是做出一个动作。
mod_rewrite的优点
用这样的一个地址重定向工具有很明显的优点,但是有一些东西也不是很明显。
人们用mod_rewrite的主要原因是为了将丑陋的、神秘的网址转化为所谓的“友好的地址”或者是“干净的地址”。新网址通过多种方式变的友好,而不是仅仅一种。 它们是用户友好的,表现在可更容易为人类所理解,瞥一眼就可以,并且用户可能自己来操纵网址。作为额外的奖励,这些网址对搜索引擎来说也是友好的。创建友好的网址是一个搜索引擎优化技术,网址是一种有效描述他链接的内容的方式。看看下面的例子:
最后的链接不仅仅是看上去变的简单了,它还可以使搜索引擎从中提取语义。这种基本的URL重写机制是使用mod_rewrite的一种方式。然而,正如你将要看到的一样,除了这些简单的转换,它还可以作很多的事情。
将同一个例子扩展一下,一些人声称通过用mod_rewrite改变你的网址可以获得安全效益。给出同一个例子,想像,考虑一下下面这个对用户id的攻击:
第一个例子是明确的PHP脚本调用,并且必须得处理无效的ID号。写得不好的脚本可能会失败,更极端的情况是(写得不好的Web应用程序)错误的输入可能导致数据损坏。然而,如果只给用户显示友好的网址,也就是说他们甚至不知道user.php网页的存在,他们可能只知道友好的URL结构。试图在这种情况下进行的攻击可能在读取PHP脚本之前就已经失败了。这是因为mod_rewrite的核心是正则表达式的格式匹配。在上面的例子中,你的地址中可能有一个数字,比如( d +),而不是字符,像a-z,当重写模块找到的是字母而不是数字时,重写就会失败。
从安全的角度讲,这种额外的抽象功能是不错的。如果你愿意,你甚至可以防止直接访问原始PHP脚。不过,我们决不能使用mod_rewrite来替换一般的安全措施,你的脚本应当在服务器端进行验证。
在服务器上启用mod_rewrite模块
就像启用.htaccess支持一样,启用mod_rewrite或者是其他apache模块必须修改全局配置文件(httpd.conf)。就像前面说的一样,由于mod_rewrite用的是如此广泛,主机提供商几乎总是启用这个模块的。然而,如果你怀疑你的主机提供商没有启用它(我们会在下面测试),你应当联系他们,并且他们很乐意启用它。
如果你是自己安装的Apache,毫无疑问,当编译Apache的时候,要将Mod_rewrite模块包括进来,因为默认情况下是不包括它的。然而,它是用的如此普遍,几乎所有的安装指南,包括Apache的安装文档都会在他们的示例中指出如何将它编译进来。然而,预先包装的版本已经将它启用了。如果你正在读这篇文章,那么你的Apache有99%的可能已经将mod_rewrite模块编译进来了,所以你只须进入下一个步骤。
如果你是你们网络的网络管理员,并且你想确认一下你已经加载了这个模块,你应当检查一下httpd.conf文件。在配置文件有很大一部分用于加载那一大堆模块。下面的行可能会出现在文件中,如果是,好极了!如果它被注释掉了,或者说是在它前面有一个#号,哪么你只需将#号删除掉,留下下面的这一部分:
1、LoadModule rewrite_module modules/mod_rewrite.so
老版本的Apache1.3,可能需要你在LoadModule目录中加上以下目录:
然而,这好像在Apache 2及以后的版本中消失了,只需要LoadModule指令。
如果你不得不修改配置文件,那么你必须重启你的apache服务。你要记住备份你的原始文件,以防万一你需要将它还原回以前的版本。
测试mod_rewrite模块
你可以通过多种方式测试mod_rewrite模块是否启用(或者是工作)了,最简单的方法是查看PHP的phpinfo函数的输出。创建下面的这个非常简单的PHP页面,在你的浏览器中打开它,并且在输出结果中找一下"mod_rewrite"。
php phpinfo(); ?>
mod_rewrite应该会显示在网页的“Loaded Modules”部分中,就像这样:
然而,如果你用的不是PHP(虽然在接下来的教程中我会用它),还有很多方式来测试。Apache有许多命令行工具。
在我的基本身份验证的第一个教程中,我提到了在htpasswd的工具。你可以使用诸如apachectl或者httpd的其他工具直接对模块进行测试。有命令行开关可以使你检查现有的已经安装加载的模块。您可以执行下面的命令来得到一个所有已加载的模块的列表。
shell> apachectl -t -D DUMP_MODULES
这里我展示的是这个命令的帮组页面。然后,我运行了这个命令,并在结果中查找了“rewrite”,有一行输出与之相匹配。
最后,如果你还是不能确定它是否启用了,像以前一样将它注释掉,看看会发生什么!之后,我会介绍语法,但这里仅仅是一个测试,看看他是否工作了。下面的.htaccess文件将重定向任何给定的文件夹请求到good.html文件,这意味着如果你的mod_rewrite工作了,你应该看到good.html。如果mod_rewrite不工作,那么你会看到一个带警告的index.html。
下面是正确的和错误的页面:
.htaccess的内容
通常情况下,你可以写在.htaccess文件中的内容也可以写到全局配置文档中。在mod_rewrite中,如果你将一条规则放的文件不同,会有一点儿小差异。最明显的是:
如果你将【……】规则放到了.htaccess文件中,目录的前缀(/)在REQUEST_URI变量中会被去掉,因为所有的请求会被自动假设是现在目录的相对地址。――Apache文档
有一点要记住,如果你在网上看例子或者是你自己在测试一个实例,要注意前面的斜线!当我将一些例子放到一起的时候,我将在下面试图澄清这些问题。
正则表达式
本教程不打算教你正则表达式。对于那些你知道的正则表达式,mod_rewrite中用到的正则表达式会根据Apache版本的不同而有所改变。在Apache 2.0中,他们似乎是与Perl兼容(pcre)的正则表达式。这意味着许多你所使用的简写,例如w的意思是[A-Za-z0-9],d的意思是[0-9],以及更多不存在的简写。但是,我的公司使用的是Apache 1.3,并且Apache1.3的正则表达式是比较有限的。
如果你不知道正则表达式,下面这些有用的教程会让你快速入门:
还有每个人都应该知道的一些引用:
如果有还没有花时间去学习正则表达式,我强烈建议你花点时间学习一下。因为通常情况下,他们没有你想象的那么复杂。我从多年的经验中选择了上面的那些关于正则表达式的链接,我觉得这些指南对于学习最基础的东西来说,写的很好。如果你想有效的利用mod_rewrite,正则表达式是至关重要的,在其他方面,了解他们也很有用,如在你最喜爱的代码编辑器中使用“查找/替换”。
初次体验
好了,你等待的耐心已经足够大了,让我们快速的看一个例子。这个例子在链接的源代码中有。这里只给出.htaccess文件的代码:
在我对它做任何解释之前,我会先讲解一下目录中的另外一个文件。
目录中包含两个文件:index.php和user.php。index.php中有一些指向user页面的链接或者是各种各样的格式。php代码用来显示页面被请求了,并检查传过来的"id"参数。下面是user.php的代码:
这个例子有一些不同的地方。首先,请注意URL重写必须通过RewriteEngine指令启用!如果你的htaccess文件要使用重写规则,应始终包括这行,否则你不能确定它是否启用了!作为一个经验法则,总是将它包括进去并确保每个.htaccess文件中你只包含了一个。字符串“on”不区分大小写,因此,当你在网上看到其他的例子用的是“On”,这是可以接受的。
第一个重写规则是用来处理user.php页面的。就像这些注释说的一样,我们正在将友好的网址重写为正常的URL格式。为了做到这一点,当输入友好的网址时,事实上,我们将它转化成了标准的查询字符串URL。将它分解开,我们就得到了:
下面是一些例子及对上面每行话的解释:
User.php
输入
匹配
提取
输出
结果
user.php?id=joe
No
user.php?id=joe
Normal
user/joe
Yes
joe
user.php?id=joe
Good
user/joe/
Yes
joe
user.php?id=joe
Good
user/joe/x
No
user/joe/x
Fail
因此,第一个例子不会受到重写规则的影响,并且可以正常访问。第二个和第三个例子与重写规则相匹配,会根据重写规则被改写,可以正常访问,最后一个例子不符合规则且无法访问。服务器没有用户目录,不能试图找到它。这是预期的结果,因为user/joe/ x是一个无法访问的网址!
这个例子比较容易理解。然而,为了澄清任何更复杂的事情,就像我现在做的一样,我必须要花好几分钟去注意细节。在下一节中,我们将举一个更复杂的例子,这个例子涉及所有重写的核心内容。
注意:如果这个例子不能在你的机器上运行,可能是由于你的Apache或mod_rewrite 版本与PCRE不兼容。请尝试着将^user/(w+)/?$改为 ^user/([a-z]+)/?$。 请注意,我没有使用w的缩写。如果此版本可以在你的机器上正确运行,那么你不要使用正则表达式的缩写,要使用较长的当量(见上面的正则表达式节)。