1.Rewrite规则简介
Rewirte主要的功能就是实现URL的跳转,它的正则表达式是基于Perl语言。可基于服务器级的(httpd.conf)和目录级的(.htaccess)两种方式。如果要想用到rewrite模块,必须先安装或加载rewrite模块。方法有两种一种是编译apache的时候就直接安装rewrite模块,别一种是编译apache时以DSO模式安装apache,然后再利用源码和apxs来安装rewrite模块。
基于服务器级的(httpd.conf)有两种方法,一种是在httpd.conf的全局下直接利用RewriteEngine on来打开rewrite功能;另一种是在局部里利用RewriteEngine on来打开rewrite功能,下面将会举例说明,需要注意的是,必须在每个virtualhost里用RewriteEngine on来打开rewrite功能。否则virtualhost里没有RewriteEngine on它里面的规则也不会生效。
基于目录级的(.htaccess),要注意一点那就是必须打开此目录的FollowSymLinks属性(httpd.conf中的Option FollowSymLinks)且在.htaccess里要声明RewriteEngine on。
2. Apache mod_rewrite规则中的标记含义
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
1) R[=code](force redirect) 强制外部重定向 强制在替代字符串加上http://thishost[:thisport]/前缀重定向到外部的URL.如果code不指定,将用缺省的302 HTTP状态码。 2) F(force URL to be forbidden)禁用URL,返回403HTTP状态码。 3) G(force URL to be gone) 强制URL为GONE,返回410HTTP状态码。 4) P(force proxy) 强制使用代理转发。 5) L(last rule) 表明当前规则是最后一条规则,停止分析以后规则的重写,不再应用其他重写规则。这个标记用于阻止当前已被重写的URL被后继规则再次重写,它对应于Perl中的last命令或C语言中的break命令。 6) N(next round) 重新从第一条规则开始运行重写过程。 7) C(chained with next rule) 与下一条规则关联 如果规则匹配则正常处理,该标志无效,如果不匹配,那么下面所有关联的规则都跳过。 8) T=MIME-type(force MIME type) 强制MIME类型 9) NS (used only if no internal sub-request) 只用于不是内部子请求 10) NC(no case) 不区分大小写 11) QSA(query string append) 追加请求字符串 12) NE(no URI escaping of output) 不在输出转义特殊字符 例如:RewriteRule /foo/(.*) /bar?arg=P1%3d$1 [R,NE] 将能正确的将/foo/zoo转换成/bar?arg=P1=zoo 13) PT(pass through to next handler) 传递给下一个处理 例如: RewriteRule ^/abc(.*) /def$1 [PT] # 将会交给/def规则处理 Alias /def /ghi 14) S=num(skip next rule(s)) 跳过num条规则 15) E=VAR:VAL(set environment variable) 设置环境变量 |
==常用变量:
详细变量介绍,查看>>Apache Rewrite服务器常用变量汇总
%{QUERY_STRING} URL参数(Query String)是URL里面“?”后面的部分,它通常用于传输参数给CGI脚本或者其它动态页面。在Apache中,该值存储在环境变量QUERY_STRING中(在PHP中,可以通过$_SERVER['QUERY_STRING']访问到),是Apache定义的“变量=值”向量(数组)。 在Apache中,大多对URL进行操作的指令,如、Redirect、Alias和RewriteRule,都不能直接访问该数据;不过,mod_rewrite模块却可以对URL参数进行添加、删除和修改。其中的关键就是使用RewriteCond来匹配%{QUERY_STRING}变量,如果需要的话,还可以使用[QSA]标志来附加URL参数。
%1、%2…%n 是反向引用,来自之前的RewriteCond中正则表达式的匹配结果
%{HTTP_HOST} 保存主机头信息,也就是当前访问的域名,即“http://”和之后第一个“/”之间的内容。如果访问地址是http://www.mrdede.com/aaa,则%{HTTP_HOST}=www.mrdede.com;如果访问http://cdn.www.mrdede.com/,%{HTTP_HOST}=cdn.www.mrdede.com
%{SERVER_PORT} 默认情况下直接输出apache的配置文件httpd.conf中的ServerName值
httpd.conf配置如下:
0 1 2 3 |
<virtualhost *> ServerName mrdede.com ServerAlias www.mrdede.com </virtualhost> |
客户端访问域名www.mrdede.com
%{HTTP_HOST} = www.mrdede.com
%{SERVER_NAME} = mrdede.com
所以,在实际程序中,应尽量使用%{HTTP_HOST} ,比较保险和可靠。
%{SERVER_PORT} 端口号
%{REQUEST_URI} 假设访问地址为http://mrdede.com/aaa/index.php?b=1&c=2,则%{REQUEST_URI}的值是http://mrdede.com后面包含正斜杠“/”在内的内容,即%{REQUEST_URI}=/aaa/index.php?b=1&c=2
%{HTTP_USER_AGENT} 是User-agent信息
==指令:
RewriteBase 定义了重写基准目录。
- 例如,如果你将虚拟站点设置在/var/www目录下,删除这行将会导致重定向到http://mrdede.com/var/www/1.php。显然这是找不到的,而且你也不会希望用户看见你的服务器的目录结构。
- 再举个例子,如果RewriteBase /base/,那么将会重定向到http://mrdede.com/base/1.php。
3.目录级(.htaccess)实例说明
例一、下面是在一个虚拟主机里定义的规则。功能是把client请求的主机前缀不是www.mrdede.cn和70.40.213.183都跳转到主机前缀为http://www.mrdede.cn,避免相同内容的网页有多个指向的域名,如http://mrdede.cn。
0 1 2 3 4 5 6 7 8 |
NameVirtualHost 70.40.213.183:80 ServerAdmin slj@mrdede.cn DocumentRoot “/web” ServerName mrdede.cn RewriteEngine on #打开rewirte功能 RewriteCond %{HTTP_HOST} !^www.mrdede.cn [NC] #声明Client请求的主机中前缀不是www.mrdede.cn,其中 [NC] 的意思是忽略大小写 RewriteCond %{HTTP_HOST} !^70.40.213.183 [NC] #声明Client请求的主机中前缀不是70.40.213.183,其中 [NC] 的意思是忽略大小写 RewriteCond %{HTTP_HOST} !^$ #声明Client请求的主机中前缀不为空 RewriteRule ^(.*) http://www.mrdede.cn/ [L] #含义是如果Client请求的主机中的前缀符合上述条件,则直接进行跳转到http://www.mrdede.cn/,[L]意味着立即停止重写操作,并不再应用其他重写规则。这里的.*是指匹配所有URL中不包含换行字符,()括号的功能是把所有的字符做一个标记,以便于后面的应用.就是引用前面里的(.*)字符。 |
例二、将输入 en.aaa.com 的域名时跳转到www.aaa.com
0 1 2 |
RewriteEngine on RewriteCond %{HTTP_HOST} ^en.aaa.com [NC] RewriteRule ^(.*) http://www.aaa.com/ [L] |
例三、近期更换了域名,新域名为www.aaa.com, 更加简短好记。这时需要将原来的域名ss.mrdede.cn, 以及论坛所在地址ss.mrdede.cn/bbs/定向到新的域名,以便用户可以找到,并且使原来的论坛 URL 继续有效而不出现 404 未找到,比如原来的http://ss.mrdede.cn/bbs/tread-60.html, 让它在新的域名下继续有效,点击后转发到http://bbs.aaa.com/tread-60.html,而其他网页,如原先的http://ss.mrdede.cn/purchase不会到二级域名bbs.aaa.com/purchase上,而是到www.aaa.com/purchase
按照这样的要求重定向规则应该这样写:
0 1 2 3 4 |
RewriteEngine On RewriteCond %{REQUEST_URI} ^/bbs/ RewriteRule ^bbs/(.*) http://bbs.aaa.com/$1 [R=permanent,L] RewriteCond %{REQUEST_URI} !^/bbs/ RewriteRule ^(.*) http://www.aaa.com/$1 [R=permanent,L] |
例四、同时达到下面两个要求:
1.用http://www.zzz.com/xxx.php 来访问 http://www.zzz.com/xxx/
2.用http://yyy.zzz.com 来访问 http://www.zzz.com/user.php?username=yyy 的功能
0 1 2 3 4 5 6 7 |
RewriteEngine On RewriteCond %{HTTP_HOST} ^www.zzz.com RewriteCond %{REQUEST_URI} !^user.php$ RewriteCond %{REQUEST_URI} .php$ RewriteRule (.*).php$ http://www.zzz.com/$1/ [R] RewriteCond %{HTTP_HOST} !^www.zzz.com RewriteRule ^(.+) %{HTTP_HOST} [C] RewriteRule ^([^.]+).zzz.com http://www.zzz.com/user.php?username=$1 |
例五、比较常用的规则
/type.php?typeid=* –> /type*.html
/type.php?typeid=*&page=* –> /type*page*.html
0 1 |
RewriteRule ^/type([0-9]+).html$ /type.php?typeid=$1 [PT] RewriteRule ^/type([0-9]+)page([0-9]+).html$ /type.php?typeid=$1&page=$2 [PT] |
例六、使用Apache的URL Rewrite配置多用户虚拟服务器
要实现这个功能,首先要在DNS服务器上打开域名的泛域名解析(自己做或者找域名服务商做)。比如,我就把 *.mrdede.us和 *.mrdede.cn全部解析到了我的IP地址70.40.213.183上。
然后,看一下我的Apache中关于*.mrdede.us的虚拟主机的设定。
0 1 2 3 4 5 6 7 8 9 10 11 12 |
ServerAdmin webmaster@mrdede.us DocumentRoot /home/www/www.mrdede.us ServerName dns.mrdede.us ServerAlias dns.mrdede.us mrdede.us *.mrdede.us CustomLog /var/log/httpd/osa/access_log.log” common ErrorLog /var/log/httpd/osa/error_log.log” AllowOverride None Order deny,allow #AddDefaultCharset GB2312 RewriteEngine on RewriteCond %{HTTP_HOST} ^[^.]+.mrdede.(cn|us)$ RewriteRule ^(.+) %{HTTP_HOST}$1 [C] RewriteRule ^([^.]+).mrdede.(cn|us)(.*)$ /home/www/www.mrdede.us/sylvan$3?un=$1&%{QUERY_STRING} [L] |
在这段设定中,我把*.mrdede.cn和*.mrdede.us 的Document Root都设定到了 /home/www/www.mrdede.us
继续看下去,在这里我就配置了URL Rewrite规则。
0 1 2 3 4 |
RewriteEngine on #打开URL Rewrite功能 RewriteCond %{HTTP_HOST} ^[^.]+.mrdede.(cn|us)$ #匹配条件,如果用户输入的URL中主机名是类似 xxxx.mrdede.us 或者 xxxx.mrdede.cn 就执行下面一句 RewriteRule ^(.+) %{HTTP_HOST}$1 [C] #把用户输入完整的地址(GET方式的参数除外)作为参数传给下一个规则,[C]是Chain串联下一个规则的意思 RewriteRule ^([^.]+).mrdede.(cn|us)(.*)$ /home/www/dev.mrdede.us/sylvan$3?un=$1&%{QUERY_STRING} [L] # 最关键的是这一句,使用证则表达式解析用户输入的URL地址,把主机名中的用户名信息作为名为un的参数传给/home/www/dev.mrdede.us目录下的脚本,并在后面跟上用户输入的GET方式的传入参数。并指明这是最后一条规则([L]规则)。注意,在这一句中指明的重写后的地址用的是服务器上的绝对路径,这是内部跳转。如果使用http://xxxx这样的URL格式,则被称为外部跳转。使用外部跳转的话,浏览着的浏览器中的URL地址会改变成新的地址,而使用内部跳转则浏览器中的地址不发生改变,看上去更像实际的二级域名虚拟服务器。 |
设置后重启Apache服务器就大功告成了!
例七、Rewrite 防盗链正则,不允许www.baidu.com和www.chinaz.com 这两个网站盗链 , 其它的网站都可以盗链.
0 1 2 3 |
RewriteEngine On RewriteCond %{HTTP_REFERER} chinaz.com [NC] RewriteCond %{HTTP_REFERER} baidu.com [NC] RewriteRule .*\.(jpg|jpeg|gif|png|rar|zip|txt|ace|torrent|gz|swf)$ http://www.xxx.com/fuck.png [R,NC,L] |
例八、通过URL参数进行访问控制
如果 http://mrdede.com/page?var=val 中的 var=val 包含字符串 foo ,则禁止访问。
0 1 |
RewriteCond %{QUERY_STRING} foo RewriteRule ^/page - [F] |
例九、删除URL参数
将URL参数直接从URL中删除。
0 |
RewriteRule ^/page /page? |
例十、增加URL参数
通过[QSA]标志保留原有URL参数的同时,在后面增加新的URL参数:var=val。
0 |
RewriteRule ^/page /page?var=val [QSA] |
例十一、重写某些URL参数
当URL参数包含字符串val时,将URL:http://mrdede.com/page1?var=val 重写为:http://mrdede.com/page2?var=val 。
0 1 |
RewriteCond %{QUERY_STRING} val RewriteRule ^/page1 /page2 |
注意,在这里如果你不修改URL参数的话,不需要使用[QSA]标志,原来的URL参数是默认附加的。
例十二、修改URL参数
当访问 /path 时,将字符串 val 修改为 other_val 。
0 1 |
RewriteCond %{QUERY_STRING} ^(.*)val(.*)$ RewriteRule /path /path?%1other_val%2 |
上面的%1和%2是反向引用,来自之前的RewriteCond中正则表达式的匹配结果。
例十三、将URL参数转换为路径
将URL:http://mrdede.com/path?var=val 转换为: http://mrdede.com/path/var/val 。
0 1 |
RewriteCond %{QUERY_STRING} ^(\w+)=(\w+)$ RewriteRule ^/path /path/%1/%2? |
注意上面的转换仅用于单一的 var=val 参数,且其中只能包含字母、数字和下划线。
例十四、将路径转换为URL参数
这是上面的转换的反向操作。这个例子可以转换有效的任意三级路径的URL: http://mrdede.com/path/var/val 将转换为 http://mrdede.com/path?var=val 。
0 |
RewriteRule ^/path/([^/]+)/([^/]+) /path?$1=$2 |
例十五、利用QSA转换查询字符串QUERY_STRING
QSA标志( Query String Appending)用于在URI中截取查询字符串,这个截取操作是通过小括号正则表达式实现的:
0 1 |
RewriteEngine On RewriteRule /pages/(.+) /page.php?page=$1 [QSA] |
将会把请求/pages/123?one=two 映射到 /page.php?page=123&one=two
注意粗体部分几乎是相同的,除了“问号”变成了“与”符号
如果没有QSA标志,那么会映射到/page.php?page=123。
如果没有用到小括号正则表达式,就不需要QSA。
小括号正则表达式可以截取查询字符串中的内容,但是如果没有开启QSA标志,那么在/page.php?page=$1中“问号”之后将会被剥离丢弃。这种特性可以用于实现“剥离查询字符串”。
通过QSA,我们可以将简单链接/simple/flat/link/ 映射成 server-side.php?first-var=flat&second-var=link
0 1 |
RewriteEngine On RewriteRule ^/([^/]+)/([^/]+)/? /index.php?first-var=$1&second-var=$2 [QSA] |
例十六、利用RewriteCond改写查询字符串QUERY_STRING
0 1 2 3 |
view sourceprint? RewriteEngine On RewriteCond %{QUERY_STRING} foo=(.*) RewriteRule ^grab(.*) /page.php?bar=%1 |
该规则将访问请求http://mysite/grab?foo=bar转换为http://mysite/page.php?bar=bar
RewriteCond用于捕获查询字符串(QUERY_STRING)中变量foo的值,并存储在%1中
QUERY_STRING是Apache定义的“变量=值”向量(数组)
例十七、QSA与RewriteCond双剑齐发
会把/grab/foobar.zip?level=5&foo=bar 映射到 /bar/index.php?file=foobar.zip&level=5&foo=bar
转换后根目录是bar目录
foobar.zip?level=5中的“问号”变成了foobar.zip&level=5中的“与”符号
0 1 2 |
RewriteEngine On RewriteCond %{QUERY_STRING} foo=(.+) RewriteRule ^grab/(.*) /%1/index.php?file=$1 [QSA] |
例十八、301重定向:去掉www
0 1 2 3 |
Options +FollowSymlinks RewriteEngine on RewriteCond %{HTTP_HOST} ^www\.(.*) [NC] RewriteRule ^(.*)$ http://%1/$1 [R=301,NC,L] |
例十九、301重定向:加上www
0 1 2 |
RewriteEngine On RewriteCond %{HTTP_HOST} ^(.*)$ RewriteRule (.*) http://www\.%1/$1 [R=301,L] |
例二十、限制某些User-agent的访问
该规则限制“迅雷”客户端下载资源,并将下载文件重置到abuse.txt
HTTP_USER_AGENT 是Apache的内置变量
2.0.50727是迅雷User-agent的特征字符串
RewriteRule后面的“点”表示“任意URI”,也就是不管请求的是什么,都输出abuse.txt
通常,我们不会仅限制一个UA。利用[OR]即可实现对多个UA作出统一处理:
0 1 2 3 4 5 |
RewriteEngine on RewriteCond %{HTTP_USER_AGENT} 2.0.50727 [NC,OR] RewriteCond %{HTTP_USER_AGENT} ^BlackWidow [NC,OR] # etc.. RewriteCond %{HTTP_USER_AGENT} ^Net\ Vampire [NC] RewriteRule . abuse.txt [L] |
例二十一、用.htaccess阻止盗链图片
解释:除本站以外其他网站都不得引用本站图片,
即:如果引用站点为“空”或者是“本站”,或者,所引用对象是“hotlink.png”,那么就允许访问
提醒:RewriteCond之间默认的逻辑连接词是逻辑“与”
0 1 2 3 4 |
RewriteEngine On RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://(www\.)?lesca\.me/ [NC] RewriteCond %{REQUEST_URI} !hotlink\.png [NC] RewriteRule .*\.(gif|jpg|png)$ /hotlink.png [NC] |
4.服务器级(httpd.conf)实例
例一、兼容网站 mrdede.com、work.mrdede.com、admin.mrdede.com 全部指向同一个目录程序#
实现:
#如果是mrdede.com 访问,直接跳转到 www.mrdede.com/$1
#如果是my.mrdede.com、test.mrdede.com 不跳转
#如果是localhost、192.168.*.*、loc、devwww本地测试的环境,不跳转
#其他全部跳转
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<Files *> Options -Indexes </Files> <IfModule mod_headers.c> <FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$"> Header set Cache-Control "max-age=2592000, public" </FilesMatch> <FilesMatch "\.(xml|txt)$"> Header set Cache-Control "max-age=604800, public" </FilesMatch> <FilesMatch "\.(html|htm|php|shtml)$"> Header set Cache-Control "max-age=86400" </FilesMatch> </IfModule> <IfModule mod_rewrite.c> Options +FollowSymlinks RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^([a-zA-Z0-9_]{2,32}\.(xml|html|txt)) seo.php?file=$1 [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} !(.*)/$ # RewriteCond %{REQUEST_URI} !\.html$ RewriteRule ^(.*)$ /$1/ [L,R=301] RewriteCond %{HTTP_HOST} ^mrdede.com [NC] RewriteRule ^(.*)$ http://www.mrdede.com/$1 [L,R=301] RewriteCond %{HTTP_HOST} !^(loc|localhost|devwww|192.168.) [NC] RewriteCond %{HTTP_HOST} !(mrdede.com)$ [NC] RewriteRule ^(.*)$ http://www.mrdede.com/$1 [L,R=301] RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] </IfModule> |