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

[经验分享] Apache的rewrite详解

[复制链接]

尚未签到

发表于 2015-12-23 14:18:45 | 显示全部楼层 |阅读模式
Apache的rewrite模块,提供了一个基于规则的重写(rewrite,也许译为重构更为合适)引擎,来实时重写发送到Apache的请求URL。因功能极其强大,被称为URL重写的“瑞士军刀”。
  这个模块使用一个基于正则表达式解析器开发的重写引擎,根据web管理员定义的规则来实时(on thefly)重写请求URL。它支持任意数目的重写规则,以及附加到一条规则上的任意数目的规则条件,从而提供了一套非常灵活和功能强大的URL处理机制。URL处理操作的实施与否,依赖于各种各样的条件检查,如检查服务器变量、环境变量、HTTP头字段、时间戳的值,甚至外部数据库的检索结果。这个模块可以在服务器范围内(http.conf)、目录范围内(.htaccess)或请求串(query-string)的一部分处理有关的URL。重写的结果URL,可以指向一个站内的处理程序、指向站外的重定向或者一个站内的代理。与灵活和功能强大相随的是设置的复杂,别指望一天内弄明白整个模块。(所以,这个学习笔记也分了几部分:)
  
内部处理过程
  API阶段
  首先,Apache处理HTTP请求是分阶段进行的,ApacheAPI为每个阶段提供了一个钩子(hook)。Mod_rewrite使用了其中的两个钩子:一个用来在HTTP请求被读取但还没有访问授权验证之前进行URL_to_filename转换,一个用来在授权验证完成且目录设置文件(.htaccess)读取之后、但内容处理器(contenthandler)被调用之前激化,进行修补(fixup).因此,当一个请求到达,Apache决定了相关的服务器(或虚拟服务器)以后进行URL_to_filename阶段,重写引擎(rewrite engine)开始处理服务器设置中的重写指令(mod_rewritedirectives).接下来几个阶段过后进入修补阶段,此时最终的数据所在的物理目录已经找到,目录配置中的重写指令开始执行。在这两个阶段,mod_rewrite都是将URL重写为新的URL或文件名,所以看起来并没有明显的区别。对API的这种应用,并不是一开始就是这样设计的,而是Apache1.x不得已而为之。为了搞清这个问题,以下两点需要记住。
  1)虽然mod_rewrite能进行URL到URL、URL到文件名字甚至文件名字到文件名字的转换,API(1.x)目前提供了一个URL_to_filename转换。在Apache2.0中,这两个钩子会被加进去,整个过程会更加清晰。一个事实必须清楚的记得:Apache在URL_to_filename钩子中,做得比API设计的功能更多。
  2)不可思议的是,mod_rewrite能在目录范围内(如根据.htaccess文件的指令配置)进行URL处理,虽然URL很早就已经被转换为文件名字了。只所以会如此,是因为.htaccess文件存在于文件系统中。也就是说,在这个阶段来进行URL处理,是非常晚的时候了。为了解决这个"先有鸡还是先有蛋"的问题,mod_rewrite用了一个小技巧:当在目录范围内处理URL/filename时,mod_rewrite先将文件名逆转回相关的URL(虽然通常是不可能的,但请参见下面用以实现这个技巧的RewriteBase指令),然后据这个新URL生成一个站内的子请求(internalsub-request),这又重开始了API进程。Mod_rewrite尽量使这些复杂的步骤对用户透明,但应要记住:虽然目录范围URL的真正处理过程很快很高效,但这一阶段会因为这个"鸡和蛋"的问题而变得很慢和低效。从另一方面来看,这也是mod_rewrite提供给普通用户进行目录范围内的URL处理的唯一途径.
规则集(RewriteRule指令集合)处理过程
  当mod_rewrite在上述的两个API阶段被激活时,它会从它的配置数据结构(在开始服务器上下文(per-servercontext)或目录上下文(per-directorycontext)时创建的)中读取配置的规则集,然后URL重写引擎启动来执行包含的规则集(一个或多条规则以及它们的条件)。两种上下文中的处理过程都是一样的,差别只是在最后的结果处理过程上。
  规则集中规则的顺序是非常重要的,因为重写引擎以特定的顺序来处理它们。重写引擎顺序遍历规则集,当一条规则匹配时,引擎会去遍历与它相关的条件集(RewriteCond指令集合).由于历史的原因,条件集先被列出来,因此控制流流程有点曲折(long-winded).如图一所示:http://hedong.3322.org/archives/pics/mod_rewrite_fig1.gif
  正如所看到的,首先URL会与每条规则的模板(pattern)比较,当匹配失败时,立即停止对当前规则的处理进入下一条规则。当匹配成功时,mod_rewrite寻找相关的规则条件。如果找不到相关的条件,则直接执行规则中定义的替换,然后回到规则遍历的过程。如果找到了相关的条件,则启动一个内部循环,依次检查各个条件。对于检查,我们不是拿一个模板来匹配当前的URL,而是先创建一个TestString串,将串内的变量、后向引用(bakc-reference)、查询结果(maplookups)等展开,然后用这个TestString和条件式中的CondPattern进行匹配,如果匹配失败,则整个条件集且这个规则都不再执行,重要回到规则遍历中;如果匹配成功,则检查下一个条件,如果所有的条件都满足,则执行规则中定义的替换动作。  
特殊字符的转义
  既然基于正则式,则当然会有特殊字符的问题。在1.3.20版本的Apache中,通过在特殊字符前加一个“\”来将TestString或Sustitution串的特殊字符转义。
正则式的后向引用
http://hedong.3322.org/archives/pics/mod_rewrite_fig2.gif  有一点需要记住:一旦在模板(pattern)或条件模板(CondPattern)中使用了括号,则后向引用已经自动产生了,你可以在Sustitution或TestString中通过$N或%N来引用相关的值。如图,描述了后向引用的值可以传到的位置。

配置指令(Configuration Directives)
  

  
指令语法默认值说明备注
RewriteEngineRewriteEngine on|offOff开关重构引擎默认时不能继承,故每个虚拟主机都要有自己的开关指令。
RewriteOptionsRewriteOptions OptionMaxRedirects=10设置一些特殊参数inherit:配置是否继承,MaxRedirects=number:内部重定向次数
RewriteLogRewriteLog file-pathNone设定重写log文件用RewriteLogLevel 0来禁止日志
RewriteLogLevelRewriteLogLevel LevelRewriteLogLevel 0设置日志级别0表示没有,2以上用于debug,9及以上表示全部信息
RewriteLockRewriteLock file-pathNone设置RewriteMap程序的同步锁文件要求是本地文件,此文件只对rewriting map-program有效。
RewriteMapRewriteMap MapName MapType:MapSourceNotused per default定义重写影射具体说明参见文档
RewriteBaseRewriteBase URL-pathphysical directory path设置目录范围内重写的基本URL具体说明参见文档
RewriteCondRewriteCond TestString CondPatternNone定义规则条件具体说明参见文档
RewriteRuleRewriteRule Pattern SubstitutionNone定义重写规则具体说明参见文档
  
参考资料:
http://httpd.apache.org/docs/mod/mod_rewrite.html
Apache的Mod_rewrite学习(二)
  今天学习重写规则的语法。
  RewriteRule
Syntax: RewriteRule Pattern Substitution [flags]
  一条RewriteRule指令,定义一条重写规则,规则间的顺序非常重要。对Apache1.2及以后的版本,模板(pattern)是一个POSIX正则式,用以匹配当前的URL。当前的URL不一定是用记最初提交的URL,因为可能用一些规则在此规则前已经对URL进行了处理。
  对mod_rewrite来说,!是个合法的模板前缀,表示“非”的意思,这对描述“不满足某种匹配条件”的情况非常方便,或用作最后一条默认规则。当使用!时,不能在模板中有分组的通配符,也不能做后向引用。
  当匹配成功后,Substitution会被用来替换相应的匹配,它除了可以是普通的字符串以外,还可以包括:

  • $N,引用RewriteRule模板中匹配的相关字串,N表示序号,N=0..9
  • %N,引用最后一个RewriteCond模板中匹配的数据,N表示序号
  • %{VARNAME},服务器变量
  • ${mapname:key|default},映射函数调用
  这些特殊内容的扩展,按上述顺序进行。
  一个URL的全部相关部分都会被Substitution替换,而且这个替换过程会一直持续到所有的规则都被执行完,除非明确地用L标志中断处理过程。
  当susbstitution有”-”前缀时,表示不进行替换,只做匹配检查。
  利用RewriteRule,可定义含有请求串(QueryString)的URL,此时只需在Sustitution中加入一个?,表示此后的内容放入QUERY_STRING变量中。如果要清空一个QUERY_STRING变量,只需要以?结束Substitution串即可。
  如果给一个Substitution增加一个http://thishost[:port]的前缀,则mod_rewrite会自动将此前缀去掉。因此,利用http://thisthost做一个无条件的重定向到自己,将难以奏效。要实现这种效果,必须使用R标志。
  Flags是可选参数,当有多个标志同时出现时,彼此间以逗号分隔。


  • 'redirect|R [=code]' (强制重定向)
      给当前的URI增加前缀http://thishost[:thisport]/, 从而生成一个新的URL,强制生成一个外部重定向(externalredirection,指生的URL发送到客户端,由客户端再次以新的URL发出请求,虽然新URL仍指向当前的服务器).如果没有指定的code值,则HTTP应答以状态值302 (MOVEDTEMPORARILY),如果想使用300-400(不含400)间的其它值可以通过在code的位置以相应的数字指定,也可以用标志名指定:temp (默认值), permanent, seeother.
      注意,当使用这个标志时,要确实substitution是个合法的URL,这个标志只是在URL前增加http://thishost[:thisport]/前缀而已,重写操作会继续进行。如果要立即将新URL重定向,用L标志来中重写流程。
  • 'forbidden|F' (强制禁止访问URL所指的资源)
      立即返回状态值403 (FORBIDDEN)的应答包。将这个标志与合适的RewriteConds 联合使用,可以阻断访问某些URL。
  • 'gone|G' (强制返回URL所指资源为不存在(gone))
      立即返回状态值410 (GONE)的应答包。用这个标志来标记URL所指的资源永久消失了.
  • # 'proxy|P' (强制将当前URL送往代理模块(proxy module))
      这个标志,强制将substitution当作一个发向代理模块的请求,并立即将共送往代理模块。因此,必须确保substitution串是一个合法的URI (如, 典型的情况是以http://hostname开头),否则会从代理模块得到一个错误.这个标志,是ProxyPass指令的一个更强劲的实现,将远程请求(remotestuff)映射到本地服务器的名字空间(namespace)中来。
      注意,使用这个功能必须确保代理模块已经编译到Apache服务器程序中了. 可以用“httpd -l”命令,来检查输出中是否含有mod_proxy.c来确认一下。如果没有,而又需要使用这个功能,则需要重新编译``httpd''程序并使用mod_proxy有效。
  • 'last|L' (最后一条规则)
      中止重写流程,不再对当前URL施加更多的重写规则。这相当于perl的last命令或C的break命令。
  • 'next|N' (下一轮)
      重新从第一条重写规则开始执行重写过程,新开的过程中的URL不应当与最初的URL相同。 这相当于Perl的next命令或C的continue命令. 千万小心不要产生死循环。
  • # 'chain|C' (将当前的规则与其后续规则??绑(chained))
      当规则匹配时,处理过程与没有??绑一样;如果规则不匹配,则??绑在一起的后续规则也不在检查和执行。
  • 'type|T=MIME-type' (强制MIME类型)
      强制将目标文件的MIME-type为某MIME类型。例如,这可用来模仿mod_alias模块对某目录的ScriptAlias指定,通过强制将该目录下的所有文件的类型改为 “application/x-httpd-cgi”.
  • 'nosubreq|NS' (used only if no internal sub-request )
      这个标志强制重写引擎跳过为内部sub-request的重写规则.例如,当mod_include试图找到某一目录下的默认文件时(index.xxx),sub-requests 会在Apache内部发生.Sub-requests并非总是有用的,在某些情况下如果整个规则集施加到它上面,会产生错误。利用这个标志可排除执行一些规则。
  • 'nocase|NC' (模板不区分大小写)
      这个标志会使得模板匹配当前URL时忽略大小写的差别。
  • 'qsappend|QSA' (追加请求串(query string))
      这个标志,强制重写引擎为Substitution的请求串追加一部分串,则不是替换掉原来的。借助这个标志,可以使用一个重写规则给请求串增加更多的数据。
  • 'noescape|NE' (不对输出结果中的特殊字符进行转义处理)
      通常情况下,mod_write的输出结果中,特殊字符(如'%', '$', ';', 等)会转义为它们的16进制形式(如分别为'%25','%24', and '%3B')。这个标志会禁止mod_rewrite对输出结果进行此类操作。 这个标志只能在 Apache1.3.20及以后的版本中使用。
  • 'passthrough|PT' (通过下一个处理器)
      这个标志强制重写引擎用filename字段的值来替换内部request_rec数据结构中uri字段的值。.使用这个标志,可以使后续的其它URI-to-filename转换器的Alias、ScriptAlias、Redirect等指令,也能正常处理RewriteRule指令的输出结果。用一个小例子来说明它的语义:如果要用mod_rewrite的重写引擎将/abc转换为/def,然后用mod_alas将/def重写为ghi,则要:
    RewriteRule ^/abc(.*) /def$1 [PT]
    Alias /def /ghi
    如果PT标志被忽略,则mod_rewrite也能很好完成工作,如果., 将 uri=/abc/... 转换为filename=/def/...,完全符合一个URI-to-filename转换器的动作。接下来 mod_alias 试图做 URI-to-filename 转换时就会出问题。
    注意:如果要混合都含有URL-to-filename转换器的不同的模块的指令,必须用这个标志。最典型的例子是mod_alias和mod_rewrite的使用。
  • 'skip|S=num' (跳过后面的num个规则)
      当前规则匹配时,强制重写引擎跳过后续的num个规则。用这个可以来模仿if-then-else结构:then子句的最后一条rule的标志是skip=N,而N是else子句的规则条数。
  • 'env|E=VAR:VAL' (设置环境变量)
      设置名为VAR的环境变量的值为VAL,其中VAL中可以含有正则式的后向引用($N或%N)。这个标志可以使用多次,以设置多个环境变量。这儿设置的变量,可以在多种情况下被引用,如在XSSI或CGI中。另外,也可以在RewriteCond模板中以%{ENV:VAR}的形式被引用。

  
注意:一定不要忘记,在服务器范围内的配置文件中,模板(pattern)用以匹配整个URL;而在目录范围内的配置文件中,目录前缀总是被自动去掉后再进行模板匹配的,且在替换完成后自动再加上这个前缀。这个功能对很多种类的重写是非常重要的,因为如果没有去前缀,则要进行父目录的匹配,而父目录的信息并不是总能得到的。一个例外是,当substitution中有http://打头时,则不再自动增加前缀了,如果P标志出现,则会强制转向代理。
注意:如果要在某个目录范围内启动重写引擎,则需要在相应的目录配置文件中设置“RewriteEngineon”,且目录的“OptionsFollowSymLinks”必须设置。如果管理员由于安全原因没有打开FollowSymLinks,则不能使用重写引擎。

Apache的Mod_rewrite学习(三)
  今天学习重写规则的条件。
  RewriteCond
Syntax: RewriteCond TestString CondPattern [flags]
  RewriteCond指令定义一条规则条件。在一条RewriteRule指令前面可能会有一条或多条RewriteCond指令,只有当自身的模板(pattern)匹配成功且这些条件也满足时规则才被应用于当前URL处理。
  TestString是一个字符串,除了包含普通的字符外,还可以包括下列的可扩展结构:


  • $N,RewriteRule后向引用,其中(0 CondPattern' (大于)
    将condPattern当作一个普通字符串,将它和TestString进行比较,当TestString 的字符大于CondPattern为真.
  • '=CondPattern' (等于)
    将condPattern当作一个普通字符串,将它和TestString进行比较,当TestString 与CondPattern完全相同时为真.如果CondPattern只是 "" (两个引号紧挨在一起) 此时需TestString 为空字符串方为真.
  • '-d' (是否为目录)
    将testString当作一个目录名,检查它是否存在以及是否是一个目录.
  • '-f' (是否是regular file)
    将testString当作一个文件名,检查它是否存在以及是否是一个regular文件.
  • '-s' (是否为长度不为0的regular文件)
    将testString当作一个文件名,检查它是否存在以及是否是一个长度大于0的regular文件
  • '-l' (是否为symbolic link)
    将testString当作一个文件名,检查它是否存在以及是否是一个 symbolic link.
  • '-F' (通过subrequest来检查某文件是否可访问)
    检查TestString是否是一个合法的文件,而且通过服务器范围内的当前设置的访问控制进行访问。这个检查是通过一个内部subrequest完成的, 因此需要小心使用这个功能以降低服务器的性能。
  • '-U' (通过subrequest来检查某个URL是否存在)
    检查TestString是否是一个合法的URL,而且通过服务器范围内的当前设置的访问控制进行访问。这个检查是通过一个内部subrequest完成的, 因此需要小心使用这个功能以降低服务器的性能。
  
  [flags]是第三个参数,多个标志之间用逗号分隔。


  • 'nocase|NC' (不区分大小写)
      在扩展后的TestString和CondPattern中,比较时不区分文本的大小写。注意,这个标志对文件系统和subrequest检查没有影响.
  • 'ornext|OR' (建立与下一个条件的或的关系)
      默认的情况下,二个条件之间是AND的关系,用这个标志将关系改为OR。例如:

    RewriteCond %{REMOTE_HOST} ^host1.* [OR]
    RewriteCond %{REMOTE_HOST} ^host2.* [OR]
    RewriteCond %{REMOTE_HOST} ^host3.*
    RewriteRule ...
    如果没有[OR]标志,需要写三个条件/规则.
  
例子:根据客户端浏览器的不同,返回不同的首页面。


RewriteCond %{HTTP_USER_AGENT} ^Mozilla.*
RewriteRule ^/$ /homepage.max.html [L]
RewriteCond %{HTTP_USER_AGENT} ^Lynx.*
RewriteRule ^/$ /homepage.min.html [L]
RewriteRule ^/$ /homepage.std.html [L]  

Apache的Mod_rewrite学习(四)
  今天学习重写影射等内容。
  RewriteMap
Syntax: RewriteMap MapName MapType:MapSource
  RewriteMap指令定义一个重写影射(RewritingMap),在规则的substitution串中,通过影射函数(mapping-functions)来查找关键字(key),并用关键字对应的值来进行来行插入或替换操作。这个查找的对象,可以是各种各样的。
  MapName是影射的名字,将用来通过下列的某种结构来为substitution定义影射函数:
${ MapName : LookupKey }
${ MapName : LookupKey | DefaultValue }
当这些结构之一出现substitution串中时,重写引擎会到mapname影射中查找lookupkey关键字,如果找到了就用返回的值(substvalue)来替换该结构,如果找不到就用defaultvalue来替换该结构,如果没有defaultvalue,就用空串来替换。
  MapType 和mapSource组合有以下几种:

标准的普通文本(Standard Plain Text)
MapType: txt, MapSource: Unix文件系统中合法的带有路径的regular file名
此种情况下,MapSource文件是一个普通的ASCII文本文件,可以含有空行、注释行(以#打头),及以下结构的键值对行(每个键值对一行)。
MatchingKey SubstValue
例如:Mapsource文件叫/path/to/file/map.txt,其内容为
##
## map.txt -- rewriting map
##
Ralf.S.Engelschall rse # Bastard Operator From Hell
Mr.Joe.Average joe # Mr. Average
在配置文件中则可以这样定义重写映射:
RewriteMap real-to-user txt:/path/to/file/map.txt
随机的普通文本(Randomized Plain Text)
MapType: rnd, MapSource: Unix文件系统中合法的带有路径的regular file名  这种情况与标准普通文本情况很相似,差别只是在SubstValue的格式上:此时的SubstValue由一些用”|”分隔的值组成的串,这个“|”是“或者”的意思。当根据键值找到对应的SubstValue后,mod_rewrite借助“|”将此串分解为一些候选项,然后随机选择一项作为最终的SubstValue的值返回。这听起来有点疯狂或毫无用处,其实这是设计用来在反向代理(reverseproxy)的情况下做负载均衡,用来查找服务器的名字。
例如:MapSource文件的名字为/path/to/file/map.txt,内容如下:

##
## map.txt -- rewriting map
##
static www1|www2|www3|www4
dynamic www5|www6
在配置文件中定义的重写影射为:
RewriteMap servers rnd:/path/to/file/map.txt
Hash File
MapType: dbm, MapSource: Unix文件系统中合法的带有路径的regular file名  这儿的Mapsource文件是一个二进制的NDBM格式的文件,含有与普通文本格式文件时相同的内容,为了实现快速查找进行了优化处理后以一种特殊的格式来表达。 可以用任何NDBM工具或者用下面的Perl脚本txt2dbm.pl来创建这种格式的文件:


#!/path/to/bin/perl
##
## txt2dbm -- convert txt map to dbm format
##  use NDBM_File;
use Fcntl;
  ($txtmap, $dbmmap) = @ARGV;

  open(TXT, "

运维网声明 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-155304-1-1.html 上篇帖子: Apache 模块开发 下篇帖子: RHCE7认证学习笔记37——Apache配置与管理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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