Apache Rewrite 详解一 入门
通常LAMP是PHP开发的黄金组合. 的确很好很强大. 不过现在流行URL要漂亮, 要符合SEO的爱好. 那么Apache的Rewrite功能就比较引人瞩目了. 找了点时间把 Apache 的文件的 rewrite 部分细细看了一下. 果然能强大也很恶心. 强大的是你想要什么样的URL就可以写什么样的URL, 恶心的是里面的正则和规则很让人吐槽. 呵呵, 废话不说了. apache rewrite 的傻瓜实例讲解有时间的话陆续写在我的Blog上. 估计得写上好几篇了. 有问题的和欢迎交流.
开始吧. 首先来个最简单的. 现在的 Zend Framework 是比较时髦了. 在网上看很多刚接触PHP网络编程的新人运行DEMO总有点小问题. 有部分是这由于服务器的rewrite模块这里的毛病. 一般搭建开发服务器的时候 WINDOW环境下要在apache 的 httpd.conf 文件里load mod_rewrite模块, 也就是把 rewrite模块前的"#" 去掉. Linux 下编译 apache 的时候记得加上带 rewrite 模块. 剩下的就没啥了. 在phpinfo()里应该可以看到apache所load的模块列表里又 rewrite 名字. 我们先看看最简单的ZF 推荐的 .htaccess 文件里的内容.
RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php
就这两行. 然后就完成了URL重写功能了. 至于参数那些. ZF框架自己搞定了. 当然也可以交给apache处理, 不过这样入门难度就高了. 估计会被人骂的. :) 关于 ZF 的路由功能. 可以参考一下 ZF 的官方文档.
大概说一下上面的意思.
首先服务器是需要支持网站使用 .htaccess 文件模式. 这个需要服务器或者虚拟主机的
RewriteEngine on 这行告诉apache服务器, 这个目录要使用URL重写功能了.
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php 这行是具体的重写规则, 以上是如果有URL来访问该目录里的文件, 如果URL不是以 .js, .ico, .gif, .jpg, .png, .css 这几种结尾的话. 那么全部调用 目录下的 index.php 文件. 这个说起来比较费劲, 搞个具体的例子. 比如我们又一个网站, 域名为: www.test.com 该站支持 .htaccess 功能. 我们把上面两行保存为 .htaccess 放到网站目录下. 如果WINDOW下不允许保存这样的文件名的话没关系. 随便保存为 x.htaccess, 然后在DOS下 rename x.htaccess .htaccess 就行了.
在www.test.com 网站根目录下放3个文件, 一个是 .htaccess, 一个是 logo.png, 一个是 index.php
测试一: 在浏览器地址栏里输入: http://www.test.com/ 很正常. 页面应该默认显示 index.php 文件的内容.
测试二: 在浏览器地址栏里输入: http://www.test.com/logo.png 也很正常. 网页里应该看到了这个png图片了.
测试三: 在浏览器地址栏里输入: http://www.test.com/test.php 这样一般来说, 如果没开重写功能的话. 通常你会看到404错误. 这说明你服务器APACHE的rewrite功能没生效. 不然你看到的是和 测试一一样的内容. 因为请求 test.php 被定向到 index.php. 其实是和访问http://www.test.com/index.php 一个效果. 这就是apache最简单的rewrite功能了.
OK, 另外再说名一下 .htaccess 文件里的一些小事情. 如果你要给指令添加注释, 防止复杂的重写规则时间常了要修改的话还要费劲去看,或者协作开发让别人能很容易看懂. 这样只要在每行的第一个字符写成"#" 就行了. 通常的指令的第一个字符是一个大写英文字母, 后面的是按照驼峰命名法写出, 比如开启重写指令: RewriteEngine, 指令值一般是小写的字符串. 像上面的 RewriteEngine on
再看一下完整的简单的.htaccess文件代码:
#该目录下使用Apache Rewrite 功能. RewriteEngine on #所有的访问请求, 除了是以 .js, .ico, .gif, .jpg, .png, .css 结尾, 都定向到index.php脚本 RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php 上面的重写规则指令用到了正则表达式. 这方面不太了解的话想去补充一下. 不然后面的部分会看起来比较费劲.
关于 RewriteRule 这个是比较能让人花点时间玩的东西了. 下篇问详细介绍它的用法和参数.
/--------------------------------------------------------------------------------------------------------------------------------------------------------
Apache Rewrite 详解二 参数标志[Flag]
上一篇聊了一下最简单的重写模式. 在进入比较好玩的重写模式之前. 先给说名一下重写规则后面的那些中括号里的大写字符是什么意思. 不然会让你看后面的代码晕头转向的. 关于这些参数的说明在 apache.org 的官方文档里有说明, 可惜没有中文的. 我大概的翻译了一下表面意思. 建议去看原E文文档. E文实在不好的先凑合着看看. 官方关于 Rewrite Flag 文档地址在: http://httpd.apache.org/docs/2.2/rewrite/rewrite_flags.html
重写规则的一般模式为下面的语法.
RewriteRule pattern target [Flag1,Flag2,Flag3]
RewriteRule 规则可以在后面添加标志(Flag), 可以又多个 flag, 多个 flag 用逗号"," 连接.
先说明一下一个比较特别的 target 值: "-", 如果目标是 "-" 的话, 那么被请求的URL不会被修改掉.
下面详细简介一下各个 flag 代表的意思.
C|chain
C|chain 意思: 字符 'C' 或者 字符串 'chain' 表示出了该行重写规则外还要有其他的重写规则, 相当于通常程序语言的 与符号 '&' , 如果第一条规则条匹配的话进行下一项条件匹配. 如果第一条或者中间一条匹配不成功. 在其后的都会被跳过.
CO|cookie
CO|cookie 意思: 字符 'CO' 或者 字符串 'cookie' 表示当某些特殊的规则被匹配到的时候, 允许设置一个COOKIE, 设置参数包含3个必须字段和2个可选字段.
三个必须的字段是设定COOKIE的名字, 值, 还有这个COOKIE的所属域名, 另外两个可选的字段是COOKIE的生存时间和路径.默认的COOKIE生存时间是浏览器的会话时间. 默认的路径是 '/', 针对整个网站.
实际的使用例子想下面这样
RewriteEngine On
# RewriteRule 匹配模式 - [CO=COOKIE名称:COOKIE值:COOKIE域名:生存时间:路径] 各个参数用冒号:连接
RewriteRule ^/index.html - [CO=mycookie:myCookieValue:.test.com:1440:/]
#RewriteRule ^/index.html - [CO=mycookie:myCookieValue:.test.com] 或者省略后面的参数.
上面的规则的意思是在请求 index.html 文件的时候设置一个COOKIE值. COOKIE名是 mycookie, 值是:myCookieValue, 生效的域名是 .test.com, 生效时间是分钟计算的. 也就是生存时间是1天=24小时=1440分钟.
E|env
E|env 意思: 字符 'E' 或者 字符串 'env' 表示你可以设置一个环境变量. 注意一下变量在这个规则运行后生效.
看一个简单的例子, 就是apache在记录日志的时候不记录图片的读取记录. 那么下面的规则就有用了.
RewriteRule \.(png|gif|jpg) - [E=image:1]
CustomLog logs/access_log combined env=!image
F|forbidden
F|forbidden 意思: 字符 'F' 或者 字符串 'forbidden' 表示禁止访问. Apache服务器会返回403禁止访问状态码给客户端.
下面的规则表示获取或者下载 exe程序文件是被显示禁止访问.
RewriteRule \.exe - [F]
G|gone
G|gone 意思: 字符 'G' 或者 字符串 'gone' 表示服务器响应状态码为:410 通常使用该标志的时候 target 目标值设置成 "-" 被请求的资源是有效的.
下面的例子表示旧的资源是有效的. 并且不在乎大小写.
RewriteRule oldproduct - [G,NC]
H|handler
H|handler 意思: 字符 'H' 或者 字符串 'handler' 表示强制使用某类型处理程序处理被请求的资源. 比如请求一些不带后缀的文件的时候. 下面的列子表示当请求的URL里没有带'.'的时候, 强制使用PHP来处理这类的请求.
RewriteRule !\. - [H=application/x-httpd-php]
L|last
L|last 意思: 字符 'L' 或者 字符串 'last' 表示当前规则是最后一条规则,停止分析以后规则的重写。该标志的使用频率非常高.
RewriteCond %{REQUEST_URI} !index\.php
RewriteRule ^(.*) index.php?req=$1 [L]
一定要注意的地方, 使用[L]标志的时候, 一定要注意你的匹配条件, 不会非常容易让你的重写规则陷入死循环, 比如你要定义页面所有页面请求都重写到一个 index.php 文件, 那么一定要注意在匹配条件时确定当请求的脚本不是index.php时才执行重写规则. 不然很明显当前页面请求的是 index.php, 当然 这个请求被重写到 index.php 然后index.php又被重写到index.php.. 这样反复执行. 页面会报错. 错误日志会记录报告你超出最大的重定向次数.
N|next
N|next 意思: 字符 'N' 或者 字符串 'next' 表示重新回到规则顶部重复执行. 一般在极端情况下用这个标志. 相当于一个while循环, 知道匹配失败时返回. 下面的例子表示把请求地址中的所有A字符替换成B字符.
RewriteRule (.*)A(.*) $1B$2 [N]
NC|nocase
NC|nocase 意思: 字符 'NC' 或者 字符串 'nocase' 表示请求的规则部分不区分大小写. 类似正则式里的/xxx/i 模式.
RewriteRule (.*\.(jpg|gif|png))$ http://images.test.com$1 [P,NC]
NE|noescape
NE|noescape 意思: 字符 'NE' 或者 字符串 'noescape' 表示不对URL中的特殊字符进行 hexcode 转码.看下面的例子:
RewriteRule ^/share/(.+) /goShare.html#$1 [NE,R]
上面的例子表示所有请求 /share/xxx.xx的请求都会被定向到/goShare.html文件上. 并且后面的部分作为一个#后面的值. 如果不加NE标志的话, #将被转义成 %23 这样就造成 404 错误了.
NS|nosubreq
NS|nosubreq 意思: 字符 'NS' 或者 字符串 'nosubreq' 表示只用于不是内部子请求.比如,在mod_include试图搜索可能的目录默认文件(index.xxx)时, Apache会内部地产生子请求。对子请求,它不一定有用的,而且如果整个规则集都起作用,它甚至可能会引发错误。所以,可以用这个标记来排除某些规则。根据你的需要遵循以下原则: 如果你使用了有CGI脚本的URL前缀,以强制它们由CGI脚本处理,而对子请求处理的出错率(或者开销)很高,在这种情况下,可以使用这个标记。
P|proxy
P|proxy 意思: 字符 'P' 或者 字符串 'proxy' 标志需要模块 mod_proxy 支持, 类似一个分发器网关的作用.比如网站的所有图片想用单独的一台服务器来运行. 那么先前的代码里的图片请求的时候, 直接定向到图片服务器去.
RewriteRule (.*)\.(jpg|gif|png) http://images.example.com$1.$2 [P]
使用[P]标志, 意味着使用了[L]标志, 因为使用该标志后马上就重定向到新地址了. 后面的重写规则会被忽略掉.
PT|passthrough
PT|passthrough 意思: 字符 'PT' 或者 字符串 'passthrough' 表示替换URL请问部分的地址.看例子
Alias /icons /usr/local/apache/icons
RewriteRule /pics/(.+)\.jpg /icons/$1.gif [PT]
当请求/pics/下的图片文件时, 实际是返回的是 /icons/目录下的同名文件. 需要注意的是一定要设置 [PT] 标志. 否则Alias设置无效.
QSA|qsappend
QSA|qsappend 意思: 字符 'QSA' 或者 字符串 'qsappend' 不怎么好表示. 看例子:
RewriteRule /pages/(.+) /page.php?page=$1 [QSA]
如果又标志: [QSA] 那么重写后的URL是: /page.php?page=123&one=two
如果没有[QSA]标志, 那么结果是: /page.php?page=123
此标记强制重写引擎在已有的替换串中追加一个请求串,而不是简单的替换。 如果需要通过重写规则在请求串中增加信息,就可以使用这个标记。
R|redirect
R|redirect 意思: 字符 'R' 或者 字符串 'redirect' 表示进行重定向, 状态码在300-399里随机出, 默认是 302 重定向.通常和标志L一起使用. 使用模式: [R[=302]]
S|skip
T|type 意思: 字符 'S' 或者 字符串 'skip' 表示跳过执行下面的几个重写规则. 又点类似goto. 看下面的例子, 如果URL请求的文件不存在的话就跳过下面的两行重写规则.
# 请求的文件是否存在
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# 不存在的情况
RewriteRule .? - [S=2]
RewriteRule (.*\.gif) images.php?$1
RewriteRule (.*\.html) docs.php?$1
T|type
T|type 意思: 字符 'T' 或者 字符串 'type' 表示为apache设置特定请求的响应类型. 也就是常说的 MIME type,比如一个perl脚本. 希望给客户端显示文本源码, 那么可以这样做:
RewriteRule \.pl$ - [T=text/plain]
或者你的服务器上的文件没有设置扩展名. 那么可以通知重写添加该文件的类型. 方便客户端显示.
RewriteRule IMG - [T=image/jpg]
以上就是Apache 官方文档提到的全部标志值及其相关的意思了. 接下来我们将介绍他们用到具体的实际例子中去. 上面代码的例子看不懂没关系. 接下来我会很详细的解释它的. :) 这篇就先到这里吧.
----------------------------------------------------------------------------------------------------------------------------------------------------------Apache Rewrite 详解三 服务器变量
上一篇日志大概了解了重写规则的标志量的意思, 这篇先把Apache 能提供给rewrite模块的服务器变量信息了解一下. 不然会在以后的重写规则中对变量的大概内容都不了解. 理解起来就比较费劲了.
需要注意的一点是在 .htaccess 文件中使用服务器参数是的写法是: %{参数名}, 比如HOST名称: %{HTTP_HOST}
Apache提供给rewirte模块的环境变量大概分成5个类型. 下面一一说明.
第一部分: HTTP headers 部分参数
参数名称: HTTP_USER_AGENT
样例参考值: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.8) Gecko/2009032609 Firefox/3.0.8
说明: 相当于PHP中的服务器参数: $_SERVER["HTTP_USER_AGENT"]
参数名称: HTTP_REFERER
样例参考值: http://www.test.cn/test.php
说明: 相当于PHP中的服务器参数: _SERVER["HTTP_REFERER"]
参数名称: HTTP_COOKIE
样例参考值: ZDEDebuggerPresent=php,phtml,php3
说明: 相当于PHP中的服务器参数: $_SERVER["HTTP_COOKIE"]
参数名称: HTTP_FORWARDED
样例参考值: 如果使用代理服务器的话会是代理服务器的IP地址, 本地不容易搭环境测试出值来.
说明: 相当于PHP中的服务器参数: $_SERVER["HTTP_FORWARDED"]
参数名称: HTTP_HOST
样例参考值: www.test.com
说明: 相当于PHP中的服务器参数: $_SERVER["HTTP_HOST"]
参数名称: HTTP_PROXY_CONNECTION
样例参考值: 网络连接代理方面的信息. 和HTTP_FORWARDED参数一样. 本地不容易搭环境测试出值来.
说明: PHP中貌似未提供这样的服务器信息值. 如果又的话可能等值于: $_SERVER["HTTP_PROXY_CONNECTION"]
参数名称: HTTP_ACCEPT
样例参考值: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
说明: 相当于PHP中的服务器参数: $_SERVER["HTTP_ACCEPT"]
第二部分: server internals 部分参数
参数名称: DOCUMENT_ROOT
样例参考值: C:/webRoot/t
说明: 相当于PHP中的服务器参数: $_SERVER["DOCUMENT_ROOT"]
参数名称: SERVER_ADMIN
样例参考值: [email protected]
说明: 相当于PHP中的服务器参数: $_SERVER["SERVER_ADMIN"]
参数名称: SERVER_NAME
样例参考值: www.test.com
说明: 相当于PHP中的服务器参数: $_SERVER["SERVER_NAME"]
参数名称: SERVER_ADDR
样例参考值: 127.0.0.1
说明: 相当于PHP中的服务器参数: $_SERVER["SERVER_ADDR"]
参数名称: SERVER_PORT
样例参考值: 80
说明: 相当于PHP中的服务器参数: $_SERVER["SERVER_PORT"]
参数名称: SERVER_PROTOCOL
样例参考值: HTTP/1.1
说明: 相当于PHP中的服务器参数: $_SERVER["SERVER_PROTOCOL"]
参数名称: SERVER_SOFTWARE
样例参考值: Apache/2.2.11 (Win32) PHP/5.2.9-1
说明: 相当于PHP中的服务器参数: $_SERVER["SERVER_SOFTWARE"]
第三部分: connection & request 部分参数
参数名称: REMOTE_ADDR
样例参考值: 127.0.0.1 正在浏览当前页面用户的 IP 地址。
说明: 相当于PHP中的服务器参数: $_SERVER["REMOTE_ADDR"]
参数名称: REMOTE_HOST
样例参考值: 127.0.0.1 正在浏览当前页面用户的主机名。反向域名解析基于该用户的 REMOTE_ADDR
说明: 相当于PHP中的服务器参数: $_SERVER["REMOTE_HOST"]
参数名称: REMOTE_PORT
样例参考值: 2574 (变化的值)用户连接到服务器时所使用的端口
说明: 相当于PHP中的服务器参数: $_SERVER["REMOTE_PORT"]
参数名称: REMOTE_USER
样例参考值: 空
说明: PHP 好像未提供相关的$_SERVER值.
参数名称: REMOTE_IDENT
样例参考值: 空
说明: PHP 好像未提供相关的$_SERVER值.
参数名称: REQUEST_METHOD
样例参考值: GET
说明: 相当于PHP中的服务器参数: $_SERVER["REQUEST_METHOD"]
参数名称: SCRIPT_FILENAME
样例参考值: C:/webRoot/t/share77.html
说明: 相当于PHP中的服务器参数: $_SERVER["SCRIPT_FILENAME"]
参数名称: PATH_INFO
样例参考值: 空
说明: 相当于PHP中的服务器参数: $_REQUEST["PATH_INFO"]
参数名称: QUERY_STRING
样例参考值: a=b&c=d&e=f
说明: 相当于PHP中的服务器参数: $_SERVER["QUERY_STRING"]
参数名称: AUTH_TYPE
样例参考值: 空 当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个是认证的类型
说明: 相当于PHP中的服务器参数: $_SERVER["AUTH_TYPE"]
第四部分: date and time 部分参数
参数名称: TIME_YEAR
样例参考值: 2009
说明: 服务器获取当前的年份值
参数名称: TIME_MON
样例参考值: 04
说明: 服务器获取当前的月份值
参数名称: TIME_DAY
样例参考值: 22
说明: 服务器获取当前的日值
参数名称: TIME_HOUR
样例参考值: 16
说明: 服务器获取当前时间的小时
参数名称: TIME_MIN
样例参考值: 26
说明: 服务器获取当前时间的分钟
参数名称: TIME_SEC
样例参考值: 34
说明: 服务器获取当前时间的秒
参数名称: TIME_WDAY
样例参考值: 3
说明: 服务器获取当天是星期几, 从星期日-星期六, 数字从 0-6
参数名称: TIME
样例参考值: 20090422162634
说明: 服务器获取当前的时间, 格式为: 年月日时分秒
第四部分: specials 部分参数
参数名称: API_VERSION
样例参考值: 20051115:21
说明: apache 的 API 版本信息.
参数名称: THE_REQUEST
样例参考值: GET /share77.html HTTP/1.1
说明: 浏览器发给服务器的请求值. 不包括其他的头信息.
参数名称: REQUEST_URI
样例参考值: /share77.html
说明: 浏览器请求的资源信息.
参数名称: REQUEST_FILENAME
样例参考值: C:/webRoot/t/share77.html
说明: 被请求的资源的在磁盘的物理地址.
参数名称: IS_SUBREQ
样例参考值: false
说明: 如果是 sub-request 则显示为 true, 否则为 false.
参数名称: HTTPS
样例参考值: off
说明: 如果连接使用 SSL/TLS 模式, 则值为on , 否则值为off, 这个参数比较安全, 即使未载入 mod_ssl 模块时.
OK, 以上就是Apache手册里提到的全部的服务器变量信息了. 这篇先到这里了. :)
==================
Apache Rewrite 详解四 杂项
前篇介绍了Apache Rewrite 的服务器变量信息. 在进入重写实战之前. 有些小细节了解一下是很又必要的.
首先一点. 如果在重写规则的正则里要使用特殊字符的时候需要在该特殊字符前带一个转义字符: 反斜杠, 比如匹配一个百分号:%, 那么正确的写法就该是: \%, 其他的特殊字符均按此写法.
另外还有官方手册里提到的四个比较重要的服务器变量. SCRIPT_NAME, SCRIPT_FILENAME, SCRIPT_URL, SCRIPT_URI
其中 SCRIPT_NAME 和 SCRIPT_FILENAME 是apache会跟踪的两个标准CGI/SSI变量, 他们包含的是物理系统视图.
官方文档的示例值:
SCRIPT_NAME=/sw/lib/w3s/tree/global/u/rse/.www/index.html
SCRIPT_FILENAME=/u/rse/.www/index.html
另外还会跟踪两个非标准的变量 SCRIPT_URL 和 SCRIPT_URI. 他们包含的是逻辑网络视图.
官方文档的示例值:
SCRIPT_URL=/u/rse/
SCRIPT_URI=http://en1.engelschall.com/u/rse/
实际上按照上面的说法看起来
SCRIPT_NAME 的值是资源请求文件在服务器的实际地址.
SCRIPT_FILENAME 资源相对于站点根目录的实际地址.
SCRIPT_URL 则是URL的相对路径.
SCRIPT_URI 则是网络请求的绝度路径.
不过在实际测试中并未发现是上面的情况. 服务器配置环境为WINXP+APACHE2.2.11+PHP-5.2.9, module模式. 和 Linux+Apache2.2.6+PHP-5.2.5, module 模式. 四个变量中 SCRIPT_FILENAME 在没有 Alians 指令下未脚本在服务器端的实际地址. SCRIPT_NAME为请求的脚本相对应网站根目录的地址, 例如:/test.php 另外两个非标准变量为空. 如果在 重写规则中使用上面的四个变量需要注意一下.
另外正则部分又个"!"表示否的意思, 其他的正则和perl的正则类似. 这方面没信息的先看看正则方面的资料. 本系列完结后分享一个重写很系统的图表. 到时候作为一个查询工具. 非常方便.
=======================
Apache Rewrite 详解五 RewriteBase
现在我们了解一下 Apache Rewrite 的重写规则的具体指令吧.
重写规则具体有 RewriteBase, RewriteCond, RewriteEngine, RewriteLock, RewriteLog, RewriteLogLevel, RewriteMap, RewriteOptions, RewriteRule 九个指令. 通常最常用的是 RewriteEngine, RewriteBase, RewriteCond, RewriteRule 四个指令. 这个我们要重点讲解的. 如果这四个指令运用的比较熟练的话, 不是特别的网站. 基本上你想出什么样的URL 都可以满足你的需要了. 其他的指令就不在讲解了. 如果对前面说的4个指令比较熟悉的话. 剩下的去看看 apache 的手册. 相信掌握这些不会费你很多脑筋.
RewriteEngine 这个指令就不用说了. 就是是否使用 Rewrite 模式的开关, 使用就设置成 on, 否则设置成 off 作用域在: server config, virtual host, directory, .htaccess
我们这篇日志专门讲解: RewriteBase
RewriteBase 的作用域为: directory, .htaccess
通常默认的虚拟主机的网站在使用.htaccess 进行重写规则时不需要执行设置该指令. 因为 RewriteBase 默认值是该 .htaccess 文件所在的目录地址.
但是如果使用目录别名的话就需要设置这个指令了. 先看官方手册的例子. 假设一个网站目录使用了别名操作: Alias /xyz /abc/def 那么当客户端访问/xyz/xxx.html 文件时是相当于访问 /abc/def/xxx.html的. 然后看看在使用了别名的情况下重写效果. 当然这个 .htaccess 文件在 /abc/def/.htaccess 位置. 内容如下:
RewriteEngine On
RewriteBase /xyz
RewriteRule ^oldstuff\.html$ newstuff.html
假设访问服务器的: /xyz/oldstuff.html (比如: http://www.test.com/xyz/oldstuff.html 这样).
服务器处理流程大概是下面的样子.
第一步, 把alias别名还原成真实的路径:
/xyz/oldstuff.html -> /abc/def/oldstuff.html (per-server Alias)
第二步, rewrite前会去掉前缀(也就是.htaccess文件所在的目录部分这里是/abc/def/), 然后执行重写规则, 处理完之后再把前缀添加上去:
/abc/def/oldstuff.html -> /abc/def/newstuff.html (per-dir RewriteRule)
第三步, 由于设定了RewriteBase值,所以路径最后还是被还原回去:
/abc/def/newstuff.html -> /xyz/newstuff.html (per-dir RewriteBase)
第四步, 重写规则完成, 别名再次使用. 最后得到的结果:
/xyz/newstuff.html -> /abc/def/newstuff.html (per-server Alias)
实际上相当于请求: /abc/def/newstuff.html, 关于 alias 的用法和意思, 请另行参考 apache 手册.
这里要注意的是即使RewriteBase /xyz 这行被注释掉. 服务器还是会执行上面的 第一步, 第二步, 后面的不会被执行, 服务器执行完第二步以后就发出一个内部重定向. 按照上面的例子也就是 GET /abc/def/newstuff.html, 由于GET 模式获取到第一个是/开头的, 相当于请求DocumentRoot目录下的/abc/def/newstuff.html, 换成客户端请求的模式也就是 http://www.test.com/xyz/oldstuff.html 变成了 http://www.test.com/abc/def/newstuff.html 如果 /xyz 只是目录的别名, DocumentRoot 目录下根本没有实际的物理目录 abc/def 这样最终会导致一个 404 报错.
另外有一点就是在进行重写规则的时候. apache 会去掉目录前缀. 注意这个时候剩下的文件名或者相对目录第一个字符不是 / 开头的. 而直接是文件名或者目录这样的模式. 关于访问 http://www.test.com 和 访问 http://www.test.com/ 又什么不同(最后加斜杠和不加斜杠). 服务器已经强制进行过了一次重定向(外部定向)了. 如果服务器未处理这类的事情的话. 就需要手动处理. 不然错误日志漫天飞. 那会是很痛苦的事情呢!
OK, 说完了. 收工先! 下篇说一下 RewriteCond.
Apache Rewrite 详解六 RewriteCond
RewriteCond 重写规则执行条件
语法: RewriteCond TestString CondPattern
生效域: server config, virtual host, directory, .htaccess
特别的上面的 TestString, 可提供反向引用. 引用模式为: %N 其中N为(0 <= N <=9), 引用当前若干RewriteCond条件中最后符合的条件中的分组成分, 也就是括号里的内容.不过用到的不多. 反向应用多在RewriteRule里常用.
RewriteCond 语法中的 TestStrng 为要被检查的内容, CondPattern 是进行匹配的规则, 它是一个兼容Perl风格的正则表达式和一些其他的特有字符属性. 这里介绍一下.
第一个: ! (感叹号) 表示否的意思. 比如一个条件: 判断访问此页面的上一页URL是否包含 sex 字符的话可以用这样: RewriteCond %{HTTP_REFERER} !(sex)
第二个: < 就是小于的意思, TestString < CondPattern.
第三个: > 就是大于于的意思, TestString < CondPattern.
第四个: = 相等的意思. <, >, = 三个和通常程序语言使用的 <, >, = 功能类似.
第五个: -d 是否是一个目录. 判断TestString是否不是一个目录可以这样: !-d
第六个: -f 是否是一个文件. 判断TestString是否不是一个文件可以这样: !-f
第七个: -s 是否是一个正常的有大小的文件. 判断TestString是否不是一个正常的有大小的文件可以这样: !-s
第八个: -l 是否是一个快捷方式文件. 判断TestString是否不是一个快捷方式文件可以这样: !-l
第九个: -x 是否是一个文件并且又执行权限. 判断TestString是否不是一个文件并且又执行权限可以这样: !-x
第十个: -F 检查TestString是否是一个合法的文件,而且通过服务器范围内的当前设置的访问控制进行访问。这个检查是通过一个内部subrequest完成的, 因此需要小心使用这个功能以降低服务器的性能。
第十一个: -U 检查TestString是否是一个合法的URL,而且通过服务器范围内的当前设置的访问控制进行访问。这个检查是通过一个内部subrequest完成的, 因此需要小心使用这个功能以降低服务器的性能.
另外: RewriteCond 指令后面可带 Flag, 现在只要2个可用, 一个是 NC|nocase, 不区分大小写的意思. 一个是 OR|ornext 表示连接下一个条件的意思.
RewriteCond 实际需要使用情况比如要判断一个条件成真的时候才执行相关的重写操作. 紧接着它下面的 RewriteRule 总是在RewriteCond 条件判断为真的时候才被执行.
看下面的一个例子:
#开启服务器重写模式
RewriteEngine on
#来自 www.test.cn 的连接访问本站时都只能访问 test.php 这页.
RewriteCond %{HTTP_REFERER} (www.test.cn)
RewriteRule (.*)$ test.php
#来自 www.test.com 的连接访问本站时都只能访问 newTest.php 这页.
RewriteCond %{HTTP_REFERER} (www.test.com)
RewriteRule (.*)$ newTest.php
OK, RewriteCond 就介绍到这里了. 其实很简单. 就想程序里的 if() 这样的效果. 下篇聊一下 RewriteRule
Apache Rewrite 详解六 RewriteRul
这两天回家了一趟. 又匆忙的赶回公司上班. 坐车那个累啊. 继续把 Apache 重写规则部分讲完.
前面的几篇关于重写的日志如果都看了的话. 这里就很容易了. 值得注意的地方是网站使用URL重写的话需要服务器配置 RewriteEngine On 和 Options FollowSymLinks, 不然重写规则不生效.
重写规则语法很简单: RewriteRule Pattern Substitution [Flag] 作用域为: server config, virtual host, directory, .htaccess
指令说明: 匹配部分(Pattern) 是正则匹配URL的正则表达式(注意特殊字符需要转义处理), 可以在替换部分(Substitution)使用反向引用匹配部分的内容. 引用模式为: $N (N为1-9的整数).
在URL重写的匹配部分中, 服务器会把请求的URL的一部分删除掉再传递给Pattern部分进行匹配. 重写结束后再添加上去. 所有平常我们看到的匹配规则总是不带网址前面的那些域名的什么东西的. 也不带什么目录什么的. 这些 apache已经给删掉了. 处理完后再加到前面. 但是有个例外就是如果 Substitution 部分是带 http:// 开头的话, 那就直接重定向了. 服务器不会把先前删除的再给加上了. 不然就出错了.
给出个形象的流程图. 见下图:
官方的例子: 重写规则写在 httpd.conf 文件里. 请求地址为: http://www.test.com/somepath/pathinfo, 看下面几种结果.
Given Rule => Resulting Substitution
-------------------------------------------------------------------------------
^/somepath(.*) otherpath$1 => 不支持, 无效的重写语句
^/somepath(.*) otherpath$1 [R] => 不支持, 无效的重写语句
^/somepath(.*) otherpath$1 [P] => 不支持, 无效的重写语句
-------------------------------------------------------------------------------
^/somepath(.*) /otherpath$1 => /otherpath/pathinfo
^/somepath(.*) /otherpath$1 [R] => http://www.test.com/otherpath/pathinfo 外部重定向
^/somepath(.*) /otherpath$1 [P] => 不支持, 很傻.
-------------------------------------------------------------------------------
^/somepath(.*) http://www.test.com/otherpath$1 => /otherpath/pathinfo
^/somepath(.*) http://www.test.com/otherpath$1 [R] => http://www.test.com/otherpath/pathinfo 外部重定向
^/somepath(.*) http://www.test.com/otherpath$1 [P] => 不支持, 很傻.
---------------------------------------------- ----------------------------------
^/somepath(.*) http://www.xxx.com/otherpath$1 => http://www.xxx.com/otherpath/pathinfo 外部重定向
^/somepath(.*) http://www.xxx.com/otherpath$1 [R] => http://www.xxx.com/otherpath/pathinfo 外部重定向 [R] 多余.
^/somepath(.*) http://www.xxx.com/otherpath$1 [P] => http://www.xxx.com/otherpath/pathinfo 内部网关重定向
同样的请求上面的地址, 如果在 .htaccess 文件里的写法. 注意和 httpd.conf 写法的区别.
比如 .htaccess 文件在目录一个虚拟主机的根目录下. 然后请求 http://www.test.com/localpath/pathinfo 看下面的几种结果.
Given Rule => Resulting Substitution
-------------------------------------------------------------------------------
^localpath(.*) otherpath$1=> /otherpath/pathinfo
^localpath(.*) otherpath$1 [R] => http://www.test.com/otherpath/pathinfo 外部重定向
^localpath(.*) otherpath$1 [P] => 不支持, 很傻.
-------------------------------------------------------------------------------
^localpath(.*) /otherpath$1 => /otherpath/pathinfo 注意 /otherpath$1 和 otherpath$1 的区别
^localpath(.*) /otherpath$1 [R] => http://www.test.com/otherpath/pathinfo 外部重定向
^localpath(.*) /otherpath$1 [P] => 不支持, 很傻.
-------------------------------------------------------------------------------
^localpath(.*) http://www.test.com/otherpath$1 /otherpath/pathinfo
^localpath(.*) http://www.test.com/otherpath$1 [R] http://www.test.com/otherpath/pathinfo 外部重定向
^localpath(.*) http://thishost/otherpath$1 [P] => 不支持, 很傻.
-------------------------------------------------------------------------------
^localpath(.*) http://www.test.com/otherpath$1 => http://www.test.com/otherpath/pathinfo 外部重定向
^localpath(.*) http://www.xxx.com/otherpath$1 [R] => http://www.xxx.com/otherpath/pathinfo 外部重定向 [R] 可省略
^localpath(.*) http://www.xxx.com/otherpath$1 [P] => http://www.xxx.com/otherpath/pathinfo 内部网关重定向
细细的看上面的例子. 重写规则还是比较简单的. 下面放出一些常用的例子. 我们使用 .htaccess 来定义重写规则. 假设域名为: www.test.com
.htaccess 放在网站根目录下. 比如: /.htaccess
例子1: 所有请求都定向到 index.php 脚本, 注意要排除 index.php 本身. 比如就进入死循环了.
RewriteRule !^index\.php$ index.php [L]
例子2: 当请求不存在的资源时, 统一定义到根目录下的 404.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /404.html [L]
例子3: 限制访问. 比如来自一些不友好的网站连接过来的请求. 不允许访问. 下例中如果 HTTP_REFERER 中包含 sex 字符, 则不允许访问.
RewriteCond %{HTTP_REFERER} sex
RewriteRule ^.*$ - [F]
例子4: 按照时间显示不同的页面, 比如访问 hello.html 页面时. 如果 在 8:00-19:00 的时候访问. 显示 hello.day.html 其他时间访问显示: hello.night.html
RewriteCond %{TIME_HOUR}%{TIME_MIN} >0700
RewriteCond %{TIME_HOUR}%{TIME_MIN} <1900
RewriteRule ^hello\.html$ hello.day.html
RewriteRule ^hello\.html$ hello.night.html
例子5: 伪静态化, 比如访问 /user20.html 则调用viewUser.php 显示用户ID为20的用户资料
RewriteRule ^user([0-9]*)\.html$ viewUser.php?userid=$1
例子6: 喜欢用二级域名的比较实用了. 比如网站目录下有 user, upload 等几个目录, 可以通过 http://www.test.com/user 这样的模式访问. 但是如果想做成统一用二级域名模式访问: http://user.test.com , 但是不允许 http://www.test.com/user 这样访问. 那么就像下面这样来限制.
RewriteCond %{REQUEST_URI} ^/user
RewriteRule ^.*$ http://user.test.net" [L]
关于重写条件的设置, 可以任意由你自己来定义. 其他的例子就不多举了. URL可以由你任意摆布.
重写还又很多高级功能, 这个需要自己慢慢的研究了. 特别关于负载均衡, 反向代理等其他很多有意思的高级功能可以慢慢去玩了. 本篇就到此为止了.
Apache重写规则的常见应用(rewrite)
一:目的
本文旨在提供怎么用Apache重写规则来解决一些常见的URL重写方法的问题,通过常见的
实例给用户一些使用重写规则的基本方法和线索。
二:为什么需要用重写规则?
一个网站,如果是长期需要放在internet上提供服务,必定会有不断地更新和维护,如临
时转移到其他服务器进行维护,重新组织目录结构,变换URL甚至改动到新的域名等等,
而为了让客户不会因此受到所有影响,最佳的方法就是使用Apache Rewrite Rule(重写
规则)。
三: 重写规则的作用范围
1) 能使用在Apache主设置文件httpd.conf中
2) 能使用在httpd.conf里定义的虚拟主机设置中
3) 能使用在基本目录的跨越设置文件.htaccess中
四:重写规则的应用条件
只有当用户的WEB请求最终被导向到某台WEB服务器的Apache后台,则这台WEB服务器接受
进来的请求,根据设置文件该请求是主设置还是虚拟主机,再根据用户在浏览器中请求的
URI来配对重写规则并且根据实际的请求路径配对.htaccess中的重写规则。最后把请求
的内容传回给用户,该响应可能有两种:
1) 对浏览器请求内容的外部重定向(Redirect)到另一个URL。
让浏览器再次以新的URI发出请求(R=301或R=302,临时的或是永久的重定向)
如:一个网站有正规的URL和别名URL,对别名URL进行重定向到正规URL,或网站改换
成了新的域名
则把旧的域名重定向到新的域名(Redirect)
2) 也可能是由Apache内部子请求代理产生新的内容送回给客户[P,L]
这是Apache内部根据重写后的URI内部通过代理模块请求内容并送回内容给客户,而客户
端浏览器并
不知道,浏览器中的URI不会被重写。但实际内容被Apache根据重写规则后的URI得到。
如:在公司防火墙上运行的Apache启动这种代理重写规则,代理对内部网段上的WEB服务
器的请求。
五:重写规则怎样工作?
我们假定在编译Apache时已把mod_rewrite编译成模块,确信你的httpd.conf中有
LoadModule rewrite_module libexec/mod_rewrite.so
并且在Addmodule中有
Addmodule mod_rewrite.c
则能使用重写规则。
当外部请求来到Apache,Apache调用重写规则中的定义来重写由用户浏览器指定请求的
URI,最后被重写的URI如果是重定向,则送由浏览器作再一次请求;如果是代理则把重写
后的URI交给代理模块请求最终的内容(Content),最后把内容送回给浏览器。
六: 何时使用.htaccess中的重写规则定义?
如果你对你的的网站内容所在的服务器没有管理员权限,或你的网站放在ISP的服务器
上托管等等条件下,你无法改写主设置文件,然而你能对你的WEB站点内容所在的目录
有写权限,则你能设置自己的.htaccess
文件达到同样的目的。但你需要确定主设置文件中对你的网站所在的目录定义了下面的内
容:
Options Indexes FollowSymLinks
AllowOverride all
否则你的.htaccess不会工作。
七: 应用举例
假定Apache被编译安装在主机192.168.1.56的/usr/local/apache/ 目录下面,我们编
译进了重写和代理模块。
1) 隐藏Apache下的某个目录,使得对该目录的所有请求都重定向到另一个文件。
a> httpd.conf的实现方法
我们放下面的部分到/usr/local/apache/conf/httpd.conf
options Indexes followsymlinks
allowoverride all
rewriteengine on
rewritebase /
rewriterule ^(.*)$ index.html.en [R=301]
注:rewriteengine on 为重写引擎开关,如果设为off,则所有重写规则定义将不被应
用,该开关的另一好处就是如果为了临时拿掉重写规则,则改为off再重启动Apache即
可,不必将下面一条条的重写规则注释掉。
rewritebase / 的作用是如果在下面的rewriterule定义中被重写后的部分(此处为文件
名index.html.en)前面没有/,则是相对目录,相对于这个rewritebase后面的定义也就
是/usr/local/apache/htdocs/index.html.en,否则,如果此处没有rewritebase /这
一项,则被重写成
http://192.168.1.56/usr/local/apache/htdocs/manual/index.html.en ,显然是
不正确的。
不过这里我们也能不用rewritebase / , 而改为
rewriteengine on
rewriterule ^(.*)$ /index.html.en [R=301]
或
rewriteengine on
rewriterule ^(.*)$ http://192.168.1.56/index.html.en [R=301]
b> .htaccess的实现方法
我们先放下面的部分到httpd.conf
options Indexes followsymlinks
allowoverride all
然后放下面的部分到/usr/local/apache/htdocs/manual/.htaccess中
rewriteengine on
rewritebase /
rewriterule ^(.*)$ index.html.en [R=301]
注:对文件.htaccess所作的所有改动不必重启动Apache.
问:要是把这个manual目录重定向到用户jephe的自己的主目录呢?
用下面的.htaccess方案。
rewriteengine on
rewritebase /~jephe/
rewriterule ^(.*)$ $1 [R=301]
则对manual目录下所有文件的请求被重定向到~jephe目录下相同文件的请求。
2) 转换www.username.domain.com的对于username的主页请求为
www.domain.com/username
对于HTTP/1.1的请求包括一个Host: HTTP头,我们能用下面的规则集重写
http://www.username.domain.com/anypath 到 /home/username/anypath
Rewriteengine on
rewritecond %{HTTP_HOST} ^www\.[^.]+\.host\.com$
rewriterule ^(.+) %{HTTP_HOST}$1 [C]
rewriterule ^www\.([^.]+)\.host\.com(.*) /home/$1$2
注:
rewritecond 条件重写规则,当满足后面定义的条件后才会应用下面的重写规则,
rewritecond有各种变量
,请查阅相关文件。
3) 防火墙上的重写规则代理内部网段上服务器的请求。
NameVirtualhost 1.2.3.4
servername www.domain.com
rewriteengine on
proxyrequest on
rewriterule ^/(.*)$ http://192.168.1.3/$1 [P,L]
注:当外部浏览器请求www.domain.com时被解析到IP地址1.2.3.4 ,Apache 交出
mod_rewrite处理转换成
http://192.168.1.3/$1后再交由代理模块mod_proxy得到内容后传送回用户的浏览器。
4) 基本预先设定的转换MAP表进行重写 rewritemap
转换www.domain.com/{countrycode}/anypath 到Map表中规定的URI,上面是虚拟主机
中的定义
rewritelog /usr/local/apache/logs/rewrite.log
rewriteloglevel 9
rewriteengine on
proxyrequest on
rewritemap sitemap txt:/usr/local/apache/conf/rewrite.map
rewriterule ^/([^/]+)+/(.*)$ http://%{REMOTE_HOST}::$1 [C]
rewriterule (.*)::([a-z]+)$ ${sitemap:$2|http://h.i.j.k