设为首页 收藏本站
查看: 964|回复: 0

[经验分享] 深入理解Apache的mod_rewrite(2)

[复制链接]

尚未签到

发表于 2018-11-28 12:15:26 | 显示全部楼层 |阅读模式
  执行流程详情
  重写规则的执行流程比较简单,但不是完全明了。因此,我将叙述一下细节。这一切都开始于用于向你的服务器提出请求的时候。他们在浏览器地址栏中键入网址,他们的浏览器将之转换成一个HTTP请求并发送到服务器,Apache收到这一请求,并将之解析成片断。下面是一个例子:
DSC0000.png

  请注意,每当我提到Apache的变量,我使用了一种奇怪的语法:%{APACHE_VAR}。这是因为它类似于mod_rewrite访问变量的语法。不过,括号内名字是重要的。
  那么,mod_rewrites是如何工作的?如果你用的是.htaccess文件,那么你只需输入REMOTE_URI部分,但没有开始的斜线!我之前提到过这个,对大多数刚刚开始用它的人来说,这显得很混乱。如果你是将它添加到了全局配置文件里,那么你应当加上斜线。
  为了说的更具体一点儿,下面是Apache的文档中对mod_rewrite中“URL部分”的描述:
  该模式始终是对请求的URL路径进行正则表达式匹配(主机名后面的那部分,但在任何以问号为标志的显示查询字符串的前面)。 Apache文档
  为了消除大家的模糊不清的认识,下面用黄色高亮显示的两个网址是mod_rewrite在.htaccess文件中的“部分网址”:
DSC0001.png

  在本节接下来的部分我将利用这两个网址来描述执行的流程。我将把第一个网址称为“绿色”网址,第二个称为“蓝色”网址。在整个分析中,我还将使用“URL部分”来表示开始处没有斜线的REMOTE_URI。
  对于那些想要100%的区分开这两中教法的人,我这里说的URL其实是URI。一个统一资源标识符(URI)的定义有别于统一资源定位符(URL)。一个URI只是标识资源在哪里,这意味着存在多个URl可以指向相同的资源,但是他们是不同的地址。一个URI可能在找到资源之前经过了数次跳动和重定向。然而,URL却是标识资源的确切位置。这种细微的差别随着时间的推移,变得越来月模糊,以至于没有人关心它们的差异。我将继续使用术语URL,因为人们用它更舒服一些。
  所以,现在我们知道重写规则将要采取行动了。一旦Apache已解析出请求,它就会将它翻译成它认为的文件,并去读取该文件。在这个过程中,他会搜索.htaccess文件。假设,.htaccess文件起用了RewriteEngine,那么任何重写规则都可以更改网址。地址的急剧变化(如Apache将某个网址原来指向的目录替换为另外一个目录)将促发Apache发出子请求,进而获取新的文件。
  在大多数情况下,你是可以看到子请求的。这些实现细节对于了解你写的或使用的大多数简单的重写规则来说并不重要。更重要的是知道Apache如何处理.htaccess文件中的重写规则。
  .htaccess 文件中的规则会以它们出现的顺序被处理。请注意,每个重写规则都是“部分网址”,也就是说类似于REMOTE_URI。当一个规则促发替换的时候,修改后的“部分网址”将被移交给下一个规则。这意味着,正在处理的网址可能已经被前面的规则修改过了,网址会被每个相匹配的规则更新。这一点很重要!
  下面是一个流程图,它试图提供URL在通过含有多个规则的.htaccess文件时的执行过程:
DSC0002.png

  请注意,流程图的顶部的将会与重写规则进行匹配的数据是“网址部分”,如果替换成功,则修改过的网址会与下一条规则继续匹配。
  前面,我介绍了重写条件,但是没有详谈。每个重写过程都与一条重写规则相关联。条件出现在与它们有联系的规则之前,但是只有与规则相匹配了,网址才会得到评估。正如流程图所示,如果与一个重写规则相匹配了,Apache会检查这条规则有什么条件(即做出替换是否需要其他条件)。如果没有条件,那么将进行替代并进入下一步。如果需要条件,那么只有所有的条件都成立的时候,才会进行替换。举一个具体的例子。
  我用的网址实际上是我放在"profile_example"目录中的源代码的一部分。这和前面的例子user.php一样,但现在有一个profile.php页面,一个附加的重写规则,和一个条件!让我们看一下这段代码和它在Apache中的执行过程:
DSC0003.png

  这里有两个规则。规则#1和我们前面看到的user例子一样。规则#2是新加的,注意它有一个条件。在“网址部分”我们已经讨论过会从上到下遍历每一条规则。因此,必须先经过规则#1,然后才是规则#2。
  理解这个例子的关键是首先要了解目标。在这个例子中,我允许友好网址,但实际上,我要明确地禁止直接访问PHP页面。请注意,有些人可能会说这是一个坏主意。他们可能会说,作为开发者,这个调试起来会更难。是这样的,事实上我不推荐做这样的小把戏,但是作为一个例子,这很好。更实际的使用mod_rewrite的例子会在本教程后面的部分看到。
  因此,在这一点的基础上,让我们看看我绿色网址发生了什么。这次,我们希望取得成功。
DSC0004.png

  在最上面,可以看到Apache的THE_REQUEST变量。我把它放在上方是因为它不像我们要处理的其他Apache变量,在请求期间这个变量的值不会改变。这就是规则#2使用%{THE_REQUEST}的原因之一。在THE_REQUEST下面,我们看到绿色的“网址部分”开始进入第一个规则了:

  • URL匹配成功。   
  • 没有任何条件,因此继续。   
  • 进行替换。   
  • 没有任何标志,因而继续。
  通过第一条规则后,该网址已经更改。网址已被重写成了profile.php?id=joe,这时,Apache会听下来更新它的大多数变量。我们看不到?id=joe,新的“网址部分”会进入下一条规则。这是我们第一次遇到条件:

  • URL匹配成功。   
  • 还有条件,我们将检查条件。   
  • THE_REQUEST不包含profile.php,因此条件检查失败。   
  • 因为不满足条件,所以我们忽略替换和标志。   
  • 这条规则没有改变URL。
  这一次,我们通过了所有的重写规则,并且profile.php?id=joe 页会被正确的提取。
  下面介绍关于如何执行蓝色的URL,这一次,我们要失败:
DSC0005.png

  我再次将THE_REQUEST的值放在了最上面,蓝色的“网址部分”进入规则#1:

  • URL匹配失败。   
  • 其他的一切都被忽略,网址没有改变,进入下一步。
  第一个规则很容易。通常情况下,如果URL匹配失败,那么它会原样进入下一步。现在进入规则#2:

  • URL匹配成功。   
  • 有条件需要比较,因此会先测试条件。   
  • 请求包含 profile.php,因此条件测试通过。   
  • 通过所有的条件,我们可以替换网址了。   
  • ”-”是一个特殊的替换,这一为着任何东西都不会改变了。   
  • 规则中有标志,因此我们处理标志。   
  • 有一个F标志,意思是返回一个禁止访问响应。   
  • 一个403 Forbidden响应发送到了客户端。
  有几件事情值得再重复一次。为了使替换发生,所有条件都必须检查通过。在上面这种情况下只有一个条件,并且检查通过了,所以,可以对网址进行替换。注意,有一种特殊的替换,不改变任何东西。当你想用标志做点儿什么的时候,这种方法相当有用,在这种情况下,我们就会这样做(指的是,替换后什么都不改变)。
  下面是一个URL例子的分解和它们的返回值表:
  Profile.php
  输入
  匹配
  获取
  输出
  结果
  profile.php?id=joe
  Yes (#2)
  profile.php?id=joe
  Forbidden
  profile/joe
  Yes (#1)
  joe
  profile.php?id=joe
  Good
  profile/joe/
  Yes (#1)
  joe
  profile.php?id=joe
  Good
  profile/joe/x
  No
  profile/joe/x
  Fail
  语法
  在介绍重写规则(RewriteRule)和重写条件(RewriteCond)的语法之前,我建议你先下载theAddedBytes Cheatsheet。这是因为cheatsheet表列出了最有用的服务器变量,标志,并有正则表达式技巧,甚至还有几个例子。在那里面有这么多的内容,将它们关联起来是很难的。
  让我们从重写规则开始。如果你想做一些特殊的事,你可以随时查看Apache的关于重写规则的文档。下面是我的概述:
DSC0006.png

  这个表显示了什么类型的标志是可用的。许多指南涵盖了flags的详细讲解,我会通过通过下面的例子介绍一下我认为的用的最多的flag。
  下面是Apache的RewriteCond文档和我的概述:
DSC0007.png

  调试流程
  当你使用mod_rewrite制定新规则的时候,总是以一个简单的规则开始,并且逐步发展为最后的版本。从来不要试图一下子将所有的事情办好。对于重写条件的编写,这个道理同样适用。一次添加规则和条件,多次测试!
  我正在介绍的这种方法的关键之处是它可以让你知道是否你的一个改变不能正常工作或者是使某个地方运行不正常。当一次做得太多的时候,你会不可避免的遇到错误,并且你将不得不恢复你所做的一切更改来找出问题到底是出在那儿了。这是一项非常艰难的 工作,可能会导致你的失望。不过,如果你总是稳步推进,并且在每一步都可以到达一个可以正常运行的点,你的处境就会稍好一点儿。
  人们往往忽略这条建议,创建了一个复杂的规则,最终却不能工作。几个小时后,他们发现问题没有出现在复杂的部分,反而只是简单的正则表达式错误,如果他们按我上面解释的构造规则的换,问题可能早已经被发现了。在反向工程拆解规则上,这种方法也适用。这种做法将极大降低人们的失望!
  例子      
  在下面的例子中,我总是会假设网站的域名是example.com。此域名很重要,因为它会影响HTTP_HOST变量以及在你的网站上将指定的URL重定向到另一个文件。如果你打算修改你的任何一个例子,以便它可以在你的网站上工作,请记住这一点。如果是这样,只需用你的域名替换“example.com”。例如,Nettuts会将“example.com”改为“nettuts.com”。
  删除www  
  这是最经典的重写规则。这将使得每个通过http://www.example.com访问你网站的人会得到一个硬性的重定向,从而其浏览器的地址栏中也将进行相应更新。

  • RewriteEngine on   
  • RewriteCond %{HTTP_HOST} ^www.example.com$ [NC]   
  • RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]
  这条规则与任何输入的地址都匹配,并将所有的地址保存为$1。本例中的重要组成部分是条件语句,这个条件会检查HTTP_HOST变量,看它是否以“www”开始。如果是这样,重写就会发生:

  • 替代的是一个完整的URL (它以http://开始)   
  • 替代中包含早期抓取的 $1。   
  • [R=301]标志将浏览器重定向到重写过的网址,在某种意义上说,这是硬性重定向,它是浏览器加载新的页面,并用新的URL地址更新地址栏。   
  • [L]标志的意思是这是最后需要分析的一条规则,重写引擎应该停止了。
  如果传入的URL是“http://www.example.com/user/index.html”,那么HTTP_HOST是beenwww.example.com,重写会创造http://example.com/user/index.html。
  如果传入的URL是“http://example.com/user/index.html”,那么HTTP_HOST是beenexample.com,不满足条件,重写引擎将会保持网址不变。
  禁止盗链
  盗链,在维基百科中被称为内联链接,是用来描述一个网站读取另一个网站的内容。通常一个网站,读取者,将包括一些其他网站上的媒体文件的链接(让我们说成是一个图像或视频)——包含内容的主机。在这种情况下,内容主机的服务器会浪费带宽为其他网站提供内容(译者注:图像、视频等)。
  对许多人来说,如果其他网站链接他们的内容,这很好。然而,许多人宁愿防止盗链,为了不支付将本网站内容发送到其他网站产生的额为的带宽。
  最常见的、基本的防止盗链是的方法将一些网站加进空白页列表,并阻止其他的一切访问。你可以通过检查引用的内容来找出谁正在从你的网站访问那些内容。 HTTP_REFERER头(是的它是这样拼写的)是由正在访问资源的浏览器或客户端设置的。最后,这是不是100%可靠的,但它是禁止大多数盗链的最有效的方法。因此,你只需验证引用是否在空白页列表中。如果引用是不能接受的(空白或其他人的网站),那么你可以给他们发送禁止警告:

  • # 给盗链着发送403禁止访问警告。   
  • RewriteEngine on   
  • RewriteCond %{HTTP_REFERER} !^http://example.net/?.*$ [NC]   
  • RewriteCond %{HTTP_REFERER} !^http://example.com/?.*$ [NC]   
  • RewriteRule .(gif|jpe?g|png|bmp)$ - [F,NC]
  在这里,RewriteRule检查的是任何一个主流类型的图像文件,例如的.gif,.png或.jpg。如果你想保护.flv,.swf或者是其他文件,你可以添加其他扩展到这个列表中。
  被允许访问的域名是“example.net”和“example.com”,在这两种情况下,重写条件验证将失败,替代也不会发生。如果有任何其他域名尝试访问,比如说说“sample.com”企图访问,那么所有的重写条件会验证通过,替代会发生,比且[F]禁止动作将被触发。
  给盗链者发送一张警告图片
  当有人试图从你的服务器上读取内容时,前面的例子会返回404禁止访问警告。实际上,你可以更进一步,给盗链者发送你选择的任何资源。例如,您可以发送一个有用的以文字“盗链不允许”表述的图片警告。这样,其他人能够意识到他们自己的错误,并在他们的主机上保存一份副本。唯一的变化是改变替换方式,并提供一个已经选好的图片来代替正在被访问的资源:

  • # 重定向盗链者请求为 "warning.png"   
  • RewriteEngine on   
  • RewriteCond %{HTTP_REFERER} !^http://example.net/?.*$   
  • RewriteCond %{HTTP_REFERER} !^http://example.com/?.*$   [NC]   
  • RewriteRule .(gif|jpe?g|png|bmp)$ http://example.com/warning.png [R,NC]
  注意,这是一个我称之为“硬”或“外部”重定向的例子。该重定向规则在他的替换部分有一个URL和一个[R]标志。
  自定义404 错误      
  一个窍门:你可以用htaccess检查目前的“URL部分”是不是链接到服务器上的实际文件或Web目录,这是一个创建自定义404“文件未找到”页面的好方法。例如,如果用户试图读取特定目录中不存在的页面时,你可以重定向它们到任何网页,如Index页面或自定义404页。

  • # 显示“custom_404.html”页的通用404页   
  • # 如果请求的页面不是一个文件或目录   
  • #静态重定向:用户的地址栏的内容不变。   
  • RewriteEngine on   
  • RewriteCond %{REQUEST_FILENAME} !-f   
  • RewriteCond %{REQUEST_FILENAME} !-d   
  • RewriteRule .* custom_404.html [L]
  这是mod_rewrite文件测试的很好的例子。它同bash shell脚本、甚至是Perl脚本文件测试相似。这里的条件检查REQUEST_FILENAME是不是一个文件或目录。在都不是的情况下,则没有这样的文件反馈给这个请求。
  如果传入的请求文件无法找到,那么返回一个“custom404.html”页面。注意有没有[R]标志,所以这是一个静态重定向,而不是硬重定向。用户的地址栏将不会改变,但网页的内容是“custom404.html”,简短而简单。
  安全第一  
  如果你有经常使用的mod_rewrite代码片段,并想轻松地分发到其他的服务器或环境中,你可能得要小心。如前所述,任何一个.htaccess文件的无效指令都可能会引起内部服务错误。因此,如果你的代码片段要移动到的环境没有mod_rewrite,你可以先暂停一下。
  一个解决这个问题是mod_rewrite模块的“检查“指令”,任何一个模块都有这个指令。只要将你的mod_rewrite代码放到块中,你可以这样设置:

  •    

  •   # Turn on   
  • RewriteEngine on   

  •   # Always remove www (with a hard redirect)   
  • RewriteCond %{HTTP_HOST} ^www.example.com$ [NC]   
  • RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]   

  •   # Generic 404 for anyplace on the site   
  •   # ...   


结论    我希望本教程能够证明mod_rewrite没有想象的那么恐怖,并且事实上通过精心设计,它的复杂性和访问速度问题都可以避免。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-640728-1-1.html 上篇帖子: windows下面apache和resin各版本、IIS的组合 下篇帖子: apache配置多个虚拟目录
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表