.htaccess探秘(三)URL重写与URL重定向

  •   
  • 3506
  • Linux
  • 8
  • super_dodo
  • 2015/11/06

URL重写与URL重定向

一、准备开始:mod_rewrite

二、利用.htaccess实现URL重写(rewrite)与URL重定向(redirect)
将.htm页面映射到.php
临时重定向(R=302)与永久重定向(R=301)
为什么要用重定向?——重定向和URL重写的区别
长短地址转换
去掉www
加上www
支持多域名访问

三、改写查询字符串QUERY_STRING
利用QSA转换查询字符串QUERY_STRING
利用RewriteCond改写查询字符串QUERY_STRING
QSA与RewriteCond双剑齐发
剥离查询字符串

四、利用RewriteCond和RewriteRule进行访问控制
文件访问控制
用.htaccess阻止User-agent
用.htaccess阻止盗链(hot-linking)
References

1. 准备开始: mod_rewrite

sudo a2enmod rewrite
<IfModule mod_rewrite.c>
    Options +FollowSymlinks
    RewriteEngine on
    # More rules below
    ...
</IfModule>

# FollowSymlinks必须启用,这是 rewrite 引擎的安全需求
# mod_rewrite 会处理所有的提交给Apache 的URL请求, 并与之后的规则进行匹配

2. 利用 .htaccess 实现URL重写和URL重定向

2.1 将.htm页面映射到.php页面

<IfModule mod_rewrite.c>
    Options +FollowSymlinks
    RewriteEngine on
    RewriteRule ^(.*)\.htm$ $1.php [NC]
</IfModule>

# [NC] No Case(不区分大小写): 对客户端请求的URL不区分大小写
# 如果通过.htm进入,浏览器地址栏显示的是.htm扩展名,但服务器上实际执行的是.php
# 必须保证服务器上有对应的.php,否则会404
# 浏览器和搜索引擎可以同时通过.htm和.php访问网页

281021121888563

test.htm
this is .htm
test.php
this is .php

2.2 临时重定向(R=302)与永久重定向(R=301)

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase /
    RewriteRule ^(.*)\.htm$ $1.php [R,NC,L]
</IfModule>

注意事项:
该RewriteRule能够将.htm静态页面重定向到.php动态页面
如果通过.htm进入,浏览器地址栏会自动转为.php,这也是重定向的本质
必须保证服务器上有对应的.php,否则会404
浏览器和搜索引擎可以同时通过.htm和.php访问网页
如果该目录上存在.htm,将被忽略
RewriteBase定义了重写基准目录。

例如,如果你将虚拟站点设置在/var/www目录下,删除这行将会导致重定向到http://yourdomain.com/var/www/1.php。显然这是找不到的,而且你也不会希望用户看见你的服务器的目录结构。
再举个例子,如果RewriteBase /base/,那么将会重定向到http://yourdomain.com/base/1.php。
对于重写基准目录,我们还可以通过将$1.php变成/$1.php实现直接变换,这时就可以将RewriteBase省略。
字母R表示临时重定向,相当于[R=302,NC]。关于重定向代码,请参考本站《HTTP协议重定向编码》
字母L表示如果能匹配本条规则,那么本条规则是最后一条(Last),忽略之后的规则。

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase /
    RewriteRule ^(.*)$ http://newdomain/$1 [R=301,NC,L]
</IfModule>

这个规则告诉浏览器和搜索引擎,网站地址发生了永久性变更,用户的URL请求将会被发送给新的域名(主机)处理。
由于是重定向到新的主机地址,RewriteBase也就没有出现的必要了。

2.3 为什么要重定向——重定向和URL重写的区别
通过重定向,浏览器知道页面位置发生变化,从而改变地址栏显示的地址
通过重定向,搜索引擎意识到页面被移动了,从而更新搜索引擎索引,将原来失效的链接从搜索结果中移除
临时重定向(R=302)和永久重定向(R=301)都是亲搜索引擎的,是SEO的重要技术
URL重写用于将页面映射到本站另一页面,若重写到另一网络主机(域名),则按重定向处理

2.4 长短地址转换


# 利用URL重写,我们可以很方便地实现长短地址的转换,但是用重定向就不合适了。
RewriteEngine On
RewriteRule ^grab /public/files/download/download.php

# 若访问
http://mysite/grab?file=my.zip

# 则会执行该页面:
http://mysite/public/files/download/download.php?file=my.zip

2.5 去掉 www

<IfModule mod_rewrite.c>
    Options +FollowSymlinks
    RewriteEngine on
    RewriteCond %{HTTP_HOST} ^www\.(.*) [NC]
    RewriteRule ^(.*)$ http://%1/$1 [R=301,NC,L]
</IFModule>

2.6 加上 www

<IfModule mod_rewrite.c>
    Options +FollowSymlinks
    RewriteEngine on
    RewriteCond %{HTTP_HOST} ^(.*)$
    RewriteRule (.*) http://www\.%1/$1 [R=301,L]
</IFModule>

2.7 支持多域名访问

如果你不凑巧买到了不支持多域名的主机,那么.htaccess或许可以帮助你。现在假设你有域名domain-one.com和domain- two.com,并且在服务器根目录有对应文件夹one和two,那么通过下面的改写就能让Apache同时接受者两个域名的请求:

#two domains served from one root..
RewriteCond %{HTTP_HOST} domain-one.com
RewriteCond %{REQUEST_URI} !^/one
RewriteRule ^(.*)$ /one/$1 [L]

RewriteCond %{HTTP_HOST} domain-two.com
RewriteCond %{REQUEST_URI} !^/two
RewriteRule ^(.*)$ /two/$1 [L]

3. 改写查询字符串 QUERY_STRING

查询字符串是指URL请求中“问号”后面的部分。比如,http://mysite/grab?foo=bar 中粗体部分就是查询字符串,其中变量名是foo,值是bar。

3.1 利用 QSA 转换查询字符串 QUERY_STRING

QSA(Query String Appending) 用于在 URI中截取查询字符串, 这个截取操作室通过小括号正则实现的

RewriteEngine On
RewriteRule /pages/(.+) /page.php?page=$1 [QSA]
RewriteRule ^/([^/]+)/([^/]+)/? /index.php?first-var=$1&second-var=$2 [QSA]

这里通过 QSA , 将简单的链接 /simple/flat/link/映射到 server-side.php?first-val=flat&second-var=link

将会把请求 /pages/123?one=two 映射到 /page.php?page=123&one=two
如果没有 QSA标志,将会映射到 /page.php?page=123
如果没有用到小括号正则,就不需要QSA,像《长短地址转换》中一样
小括号正则表达式可以截取查询字符串中的内容,但是如果没有开启QSA标志,那么 /page.php?page=$1中 问号之后的部分将会剥离丢弃。

3.2 利用RewriteCond改写查询字符串QUERY_STRING

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定义的“变量=值”向量(数组)

3.3 QSA与RewriteCond双剑齐发

RewriteEngine On
RewriteCond %{QUERY_STRING} foo=(.+)
RewriteRule ^grab/(.*) /%1/index.php?file=$1 [QSA]

会把 /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中的“与”符号

3.4 剥离查询字符串

只需在要开始剥离的链接后面加个“问号”,并且不要启用QSA标志,就可剥离查询字符串

RewriteEngine On
# Whatever QS is
RewriteCond %{QUERY_STRING} . 
# I don't want it with Question mark
RewriteRule foo.php(.*) /foo.php? [L]

4. 利用 RewriteCond 和 RewriteRule进行访问控制

我们在第一篇.htaccess基础中提到了很多有用的访问控制方法,其实通过Rewrite也能实现类似的功能,而且可以更强大!

4.1 文件访问控制

之前利用Order、Files及FilesMatch命令实现的访问控制可以满足大部分要求,但是当用户被拒绝时,他们看到的是硕大的“403 Forbidden”,如果你不想伤害用户的感情,就需要显示一些别的东西,通过Rewrite就可以实现这个特性:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !^(.+)\.css$
RewriteCond %{REQUEST_FILENAME} !^(.+)\.js$
RewriteCond %{REQUEST_FILENAME} !special.zip$
RewriteRule ^(.+)$ /chat/ [NC]

该规则将仅允许用户请求.css, .js类型的文件,还有special.zip文件
RewriteRule 后面指定了限制规则:映射到/char/目录下处理
RewriteCond 后面的“感叹号”(!)起到了“否定”作用,它表明,对不满足后面正则表达式者应用RewriteRule规则,也就是对当前类型的文件将不应用规则
RewriteCond 之间是以逻辑“与”连接的,也就是只有当三个条件都不满足时才执行RewriteRule
该规则也会限制访问.htm, .jpg等格式
该规则不可以放在虚拟站点根目录(/)下,否则会死循环
如果是二级目录,如/test/,那么传入RewriteCond的参数是以/test/开始的,因此从(.+)获得的文件名也含有/test/,读者必须对此多加小心
要想仅获得文件名,可以将(.+)替换成([^/]+),并且去掉符号^,如下所示:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !([^/]+)\.css$
RewriteCond %{REQUEST_FILENAME} !([^/]+)\.js$
RewriteRule ^(.+)$ /chat/ [NC]

4.2 用.htaccess阻止User-agent

什么是User-agent?User-agent用于浏览器向服务器“自报家门”,更确切的说是所有HTTP客户端都得用User-agent向 服务器“自报家门”,以便服务器对不同的客户端作出不同响应。比如,某站点可能需要对浏览器、搜索引擎crawl还有各类下载工具作出不同的响应。服务器 就是通过所谓的User-agent进行区分的。
如果你的服务器提供某些资源的下载,那么你就必须多加小心诸如“迅雷”等下载软件,因为它们可能把你网站资源吸干,并且影响你的正常访客访问。为此,我们可以利用Rewrite限制某些UA的访问:

RewriteEngine on
RewriteCond %{HTTP_USER_AGENT} 2.0.50727 [NC]
RewriteRule . abuse.txt [L]

该规则限制“迅雷”客户端下载资源,并将下载文件重置到abuse.txt
HTTP_USER_AGENT是Apache的内置变量
2.0.50727是迅雷User-agent的特征字符串
RewriteRule后面的“点”表示“任意URI”,也就是不管请求的是什么,都输出abuse.txt
通常,我们不会仅限制一个UA。利用[OR]即可实现对多个UA作出统一处理:

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]

4.3 用.htaccess阻止盗链(hot-linking)

盗链,特别是图片,是非常可耻的!哪怕将图片复制到自己服务器上,也比盗用他人的图片链接来得光彩!(吐糟完毕)
.htaccess的Rewrite功能可以提供非常简单、有效的方法阻止这种可耻行为:

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]

简单解释一下该规则的功能:除本站以外其他网站都不得引用本站图片,具体可以理解为如果引用站点为“空”或者是“本站”,或者,所引用对象是“hotlink.png”,那么就允许访问再次提醒,RewriteCond之间默认的逻辑连接词是逻辑“与”这里的难点是理解逻辑转换,即德·摩根定律.

htaccess探秘系列相关延伸阅读

.htaccess探秘(一)访问控制(Allow/Deny)

.htaccess探秘(二)目录浏览与主页

.htaccess探秘(三)URL重写与URL重定向

.htaccess探秘(四)htaccess正则表达式

人生的烦恼,多在于知道的太多,而做的太少。