Aapache重写规则
.htaccess叫分布式配置文件,.htaccess文件的作用在于当浏览器通过uri访问到服务器某个文件夹或文件时,我们可以决定这个uri是否访问uri指定资源或者拒绝访问。并且通过一个十分友好的uri吸引用户进来,并且通过.htaccess文件把用户导向服务器指定的访问位置。它提供了针对目录改变配置的方法——在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。并且子目录中的指令会覆盖更高级目录或者主服务器配置文件中的指令。说的通俗点,他就是:一个更改服务器配置的‘文本’文件。一般来说,如果你的虚拟主机使用的是Unix或Linux系统,或者任何版本的Apache网络服务器,从理论上讲都是支持.htaccess的。他可以实现包括:文件夹密码保护、用户自动重定向、自定义错误页面、改变你的文件扩展名、封禁特定IP地址的用户、只允许特定IP地址的用户、禁止目录列表,以及使用其他文件作为index文件等等。
前提
必须开启apache里面的重写模块!
- 第一步,首先进入apache安装目录下,找到httpd.conf配置文件,并打开httpd.conf。
# 找到下面这行代码,取消其注释符号 ‘#’。
# LoadModule reqtimeout_module libexec/apache2/mod_reqtimeout.so
第二步,找到所有的
AllowOverride denied
并改为AllowOverride all
——意思就是支持重写。第三步,重启apache服务器。
.htaccess基本语法介绍
基本语句
语句 | 含义 |
---|---|
RewriteEngine on | 开启apache重写功能 |
RewriteBase / | 设置重写根目录 |
RewriteCond | rewrite condition 重写条件,匹配符合条件——正则的请求 |
RewriteRule | 重定向 |
RewriteBase
RewriteEngine on
RewriteBase /
RewriteRule ^(.*)\.htm$ $1.php [R,NC,L]
RewriteBase定义了重写基准目录。
例如,如果你将虚拟站点设置在/var/www
目录下,删除这行将会导致重定向到http://yourdomain.com/var/www/1.php
。显然这是找不到的,而且你也不会希望用户看见你的服务器的目录结构。
再举个例子,如果RewriteBase /base/
,那么将会重定向到http://yourdomain.com/base/1.php
。
对于重写基准目录,我们还可以通过将$1.php
变成/$1.php
实现直接变换,这时就可以将RewriteBase省略。
RewriteCond说明
语法
【说明 】定义重写发生的条件
【语法】 RewriteCond 【TestString】 【CondPattern】 [flags]
【作用域】 server config, virtual host, directory, .htaccess
【覆盖项】 FileInfo
【状态】 扩展(E)
【模块】 mod_rewrite
注意事项
可以在CondPattern串的开头使用’!'(惊叹号)来指定不匹配。
-
CondPatterns有若干特殊的变种。除了正则表达式的标准用法,还有下列用法:
‘<CondPattern’(词典顺序的小于) 将CondPattern视为纯字符串,与TestString按词典顺序进行比较。如果TestString小于CondPattern则为真。 ‘>CondPattern’(词典顺序的大于) 将CondPattern视为纯字符串,与TestString按词典顺序进行比较。如果TestString大于CondPattern则为真。 ‘=CondPattern’(词典顺序的等于) 将CondPattern视为纯字符串,与TestString按词典顺序进行比较。如果TestString等于CondPattern(两个字符串逐个字符地完全相等)则为真。如果CondPattern是”"(两个双引号),则TestString将与空字符串进行比较。 ‘-d’(目录) 将TestString视为一个路径名并测试它是否为一个存在的目录。 ‘-f’(常规文件) 将TestString视为一个路径名并测试它是否为一个存在的常规文件。 ‘-s’(非空的常规文件) 将TestString视为一个路径名并测试它是否为一个存在的、尺寸大于0的常规文件。 ‘-l’(符号连接) 将TestString视为一个路径名并测试它是否为一个存在的符号连接。 ‘-x’(可执行) 将TestString视为一个路径名并测试它是否为一个存在的、具有可执行权限的文件。该权限由操作系统检测。 ‘-F’(对子请求存在的文件) 检查TestString是否为一个有效的文件,而且可以在服务器当前的访问控制配置下被访问。它使用一个内部子请求来做检查,由于会降低服务器的性能,所以请谨慎使用! ‘-U’(对子请求存在的URL) 检查TestString是否为一个有效的URL,而且可以在服务器当前的访问控制配置下被访问。它使用一个内部子请求来做检查,由于会降低服务器的性能,所以请谨慎使用!
-
还可以在CondPattern之后追加特殊的标记[flags]作为RewriteCond指令的第三个参数。flags是一个以逗号分隔的以下标记的列表:
-
‘nocase|NC’(忽略大小写)
它使测试忽略大小写,扩展后的TestString和CondPattern中’A-Z’ 和’a-z’是没有区别的。此标记仅用于TestString和CondPattern的比较,而对文件系统和子请求的检查不起作用。
-
‘ornext|OR’(或下一条件)
它以OR方式组合若干规则的条件,而不是隐含的AND。
RewriteCond %{REMOTE_HOST} ^host1.* [OR] RewriteCond %{REMOTE_HOST} ^host2.* [OR] RewriteCond %{REMOTE_HOST} ^host3.* RewriteRule … 针对这3个主机的规则集 …如果不用这个标记,你就必须要书写三次条件/规则对。
-
RewriteRule配置参数
语句 | 含义 |
---|---|
R | 强制外部重定向;R=301 永久重定向、R=302 临时重定向 |
F | 禁止URL,返回403 http状态码 |
G | 强制URL为GONE,返回410 http状态码 |
P | 强制使用代理转发 |
L | 表明当前规则是最后一条规则,停止分析以后的规则重写 |
N | 重新从第一条规则开始运行重写过程 |
C | 与下一条规则关联 |
T=MIME-type(force MIME type) 强制MIME类型 | |
NS | 只是用于不是内部的子请求 |
NC | 不区分大小写 |
QSA | 追加请求字符串 |
NE | 不再输出转义特殊字符 %3d$1 等价于 =$1 |
生效条件
【说明 】定义重写发生的条件
【语法】 RewriteCond 【TestString】 【CondPattern】 [flags]
【作用域】 server config, virtual host, directory, .htaccess
【覆盖项】 FileInfo
【状态】 扩展(E)
【模块】 mod_rewrite
RewriteCond指令定义了规则生效的条件,即在一个RewriteRule指令之前可以有一个或多个RewriteCond指令。条件之后的重写规则仅在当前URI与Pattern匹配并且满足此处的条件(TestString能够与CondPattern匹配)时才会起作用。
- RewriteRule反向引用 ,引用方法是:$N (0 <= N <= 9)引用当前(带有若干RewriteRule指令的)RewriteCond中的与Pattern匹配的分组成分(圆括号!)。
- RewriteCond反向引用 ,引用方法是:%N (1 <= N <= 9)引用当前若干RewriteCond条件中最后符合的条件中的分组成分(圆括号!)。
- RewriteMap扩展 ,引用方法是:${mapname:key|default} 细节请参见RewriteMap 指令。
- 服务器变量 ,引用方法是:%{NAME_OF_VARIABLE} NAME_OF_VARIABLE可以是下节apache环境变量列出的字符串之一:
apache环境变量
获取环境变量
在上一节中讲述了 %{}
代表获取环境变量;可以获取的环境变量中的任何变量。具体内容还需要参见官方文档中——重写模块,要分析环境变量最好的方法phpinfo();访问地址写全一点http://www.fuermao.com/index.php?1111#111
,环境变量的类型如下表(phpinfo()
所获取的并不是特别全):
官方其他参数说明
IS_SUBREQ
如果正在处理的请求是一个子请求,它将包含字符串”true”,否则就是”false”。模块为了解析URI中的附加文件,可能会产生子请求。
API_VERSION
这是正在使用中的Apache模块API(服务器和模块之间内部接口)的版本, 其定义位于include/ap_mmn.h中。此模块API版本对应于正在使用的Apache的版本(比如在Apache 1.3.14的发行版中这个值是19990320:10)。 通常,对它感兴趣的是模块的开发者。
THE_REQUEST
这是由浏览器发送的完整的HTTP请求行(比如:”GET /index.html HTTP/1.1″)。它不包含任何浏览器发送的其它头信息。
REQUEST_URI
这是在HTTP请求行中所请求的资源(比如上述例子中的”/index.html”)。
REQUEST_FILENAME
这是与请求相匹配的完整的本地文件系统的文件路径名。
HTTPS
如果连接使用了SSL/TLS,它将包含字符串”on”,否则就是”off”(无论mod_ssl是否已经加载,该变量都可以安全的使用)。
注意事项
SCRIPT_FILENAME和REQUEST_FILENAME包含的值是相同的——即Apache服务器内部的request_rec结构中的filename字段。 第一个就是大家都知道的CGI变量名,而第二个则是REQUEST_URI(request_rec结构中的uri字段)的一个副本。
特殊形式:%{ENV:variable} ,其中的variable可以是任意环境变量。它是通过查找Apache内部结构或者(如果没找到的话)由Apache服务器进程通过getenv()得到的。
特殊形式:%{SSL:variable} ,其中的variable可以是一个SSL环境变量的名字,无论mod_ssl模块是否已经加载都可以使用(未加载时为空字符串)。比如:%{SSL:SSL_CIPHER_USEKEYSIZE}将会被替换为128。
特殊形式:%{HTTP:header} ,其中的header可以是任意HTTP MIME头的名称。它总是可以通过查找HTTP请求而得到。比如:%{HTTP:Proxy-Connection}将被替换为Proxy-Connection:HTTP头的值。
预设形式:%{LA-U:variable} ,variable的最终值在执行一个内部(基于URL的)子请求后确定。 当需要使用一个目前未知但是会在之后的过程中设置的变量的时候,就可以使用这个方法。例如,需要在服务器级配置(httpd.conf文件)中根据REMOTE_USER变量进行重写, 就必须使用%{LA-U:REMOTE_USER}。因为此变量是由URL重写(mod_rewrite)步骤之后的认证步骤设置的。 但是另一方面,因为mod_rewrite是通过API修正步骤来实现目录级(.htaccess文件)配置的, 而认证步骤先于API修正步骤,所以可以用%{REMOTE_USER}。
预设形式:%{LA-F:variable} ,variable的最终值在执行一个内部(基于文件名的)子请求后确定。 大多数情况下和上述的LA-U是相同的。
CondPattern是条件模式,即一个应用于当前TestString实例的正则表达式。TestString将被首先计算,然后再与CondPattern匹配。
调试重写模块
关于调试信息
关于调试这部分由于不能够直接打印和输出,所以需要在apache的日志中去查找相关信息。apache的日志大致分为两类,如:
- access_log:访问或请求apache服务日志的访问信息;
- error_log:apache服务器出错时的,日志记录错误信息;
日志一般都在httpd.conf
文件的相关位置有路径,当然可以改变日志记录的路径;
# 错误日志
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here. If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
# 这个地方时我修改后的错误日志记录信息位置
ErrorLog "/Users/futianyu/log/apache/error_log"
# 这个地方是日志记录级别:一共有8个级别;
# LogLevel: Control the number of messages logged to the error_log.
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
#
# 暂时注释掉
# LogLevel warn
# 重写调试时打开,因为要调试重写模块
LogLevel info rewrite:trace5
#
# The location and format of the access logfile (Common Logfile Format).
# If you do not define any access logfiles within a <VirtualHost>
# container, they will be logged here. Contrariwise, if you *do*
# define per-<VirtualHost> access logfiles, transactions will be
# logged therein and *not* in this file.
# 这个地方就是访问日志,当然我也改了路径了;
CustomLog "/Users/futianyu/log/apache/access_log" common
设置日志输出级别
此处我们需要查看的是重写模块调试时的错误信息,给个官方文档的连接;重写模块日志记录中的Per-module logging部分有说明;改了写配置文件需要重启httpd服务器,不然不会生效。在上面的代码中文档已经给出了日志级别的说明:debug, info, notice, warn, error, crit,alert, emerg;
示例
示例一
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?xxx\.com$
RewriteCond %{REQUEST_URI} !^/blog/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /blog/$1
# 没有输入文件名的默认到到首页
RewriteCond %{HTTP_HOST} ^(www\.)?xxx\.com$
RewriteRule ^(/)?$ blog/index.php [L]
第一部分 重写规则
-
RewriteEngine On
表示重写引擎开,关闭Off,作用就是方便的开启或关闭以下的语句。
-
RewriteCond %{HTTP_HOST} \^(www\.)?xxx\.com$
RewriteCond [匹配的字符串] [正则条件]
%{}
在上面也说明了,代表获取的系统变量;
这条语句表示重写条件,如果【匹配字符串】符合【正则条件】,则执行后面的语句;
这里需要指出的是,HTTP_HOST
变量是会携带端口号(默认80端口是不会携带的),且无http://
协议格式。
获取的访问URL中的www.xxx.com
,如果符合正则条件则,继续执行下面的语句。 -
`RewriteCond %{REQUEST_URI} !^/blog/``
获取请求资源虚拟地址,
REQUEST_URI
是域名后至hash(#)前的部分,例如下面的网址:
http://www.baidu.com/aa/bb/cc.php?dd=111#ee
那么REQUEST_URI
所获取的部分就是aa/bb/cc.php?dd=111
。
回到正题上,如果获取到的虚拟地址信息不是以/blog/
开头(正则前面有个!
),则执行下面的条件; -
RewriteCond %{REQUEST_FILENAME} !-f
与RewriteCond %{REQUEST_FILENAME} !-d
这两条语句就一起讲了,如果请求的文件不是文件,不是目录的话,就执行下面的重写规则
RewriteRule ^(/)?$ blog/index.php [L]
。 -
RewriteRule ^(.*)$ /blog/$1
这条语句是整个重写的终点也是难点部分,意思是当上面的RewriteCond条件都满足的时候,将会执行此重写规则;
^(.*)$
匹配的是当前请求URL地址,整个地址都匹配,所以是.*
代表表示任意单个字符,*表示匹配0次或N次(N>0);后面的/blog/$1
就是重写部分,意思是将前面匹配的字符重写成/blog/$1
。这个$1
表示反向匹配,引用的是前面第一个圆括号的成分,即^(.*)
$中 的.*
。这里出现一个问题。
【注意】
如果请求地址是:http://www.xxx.com/a.html
,到底是匹配整个http://www.xxx.com/a.html
,还是只匹配/a.html
即反斜杠后面的成分,还是只匹配a.html
?根据RewriteBase规则规定,如果Rewritebase 为
/
,将会匹配a.html
,不带前面的反斜杠,所以上条语句应该写成RewriteRule^(.*)$ blog/$1
(不带/)而不是/blog/$1
。现在问题出来了,如果不设置Rewritebase
为/
,将会匹配整个网址http://www.xxx.com/a.html
,显然这是错误的,所以应该添加这条:RewiteBase /
。
第二部分 重写规则
RewriteCond %{HTTP_HOST} ^(www\.)?xxx\.com$
-
RewriteRule ^(/)?$ blog/index.php [L]
这两句的意思是指请求的host地址是
www.xxx.com
,如果地址的结尾只有0个或者1个“/”时,将会重写到子目录下的主页,主要因为重写后的地址是不能自动寻找主页的,需要自己指定。
正确答案
RewriteEngine On
RewiteBase /
RewriteCond %{HTTP_HOST} ^(www\.)?xxx\.com$ [NC]
RewriteCond %{REQUEST_URI} !^/blog/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ blog/$1
# 没有输入文件名的默认到到首页
RewriteCond %{HTTP_HOST} ^(www\.)?xxx\.com$ [NC]
RewriteRule ^(/)?$ blog/index.php [L]
示例二 将.htm页面映射到.php
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^(.*)\.htm$ $1.php [NC]
注意事项:
- 该RewriteRule能够将.htm静态页面映射到.php动态页面
- 如果通过.htm进入,浏览器地址栏显示的是.htm扩展名,但服务器上实际执行的是.php
- 必须保证服务器上有对应的.php,否则会404
- 浏览器和搜索引擎可以同时通过.htm和.php访问网页
- 如果该目录上存在.htm,将被忽略
- [NC]表示“不区分大小写”,更多类似定义请参考本站《.htaccess正则表达式》一文
示例三 长短地址转换
若访问http://mysite/grab?file=my.zip
如何实际访问至http://mysite/public/files/download/download.php?file=my.zip
;
RewriteEngine On
RewriteRule ^grab /public/files/download/download.php
示例四 支持多域名访问
现在假设你有域名domain-one.com和domain-two.com,并且在服务器根目录有对应文件夹one和two,那么通过下面的改写就能让Apache同时接受者两个域名的请求:
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]
示例五 去掉www
Options +FollowSymlinks
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.(.*) [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,NC,L]
示例六 加上www
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(.*)$
RewriteRule (.*) http://www\.%1/$1 [R=301,L]
参考资料
《.htaccess 基础教程(四)Apache RewriteCond 规则参数》 http://www.cnblogs.com/52php/p/5670199.html
《 .htaccess技巧: URL重写(Rewrite)与重定向(Redirect)》 https://blog.csdn.net/newjueqi/article/details/12014673 维摩诘