本文档是mod_rewrite
参考文档的补充。它描述了如何使用mod_rewrite
重定向和重新映射请求。这包括mod_rewrite常用用法的许多示例,包括每种用法的详细说明。
DocumentRoot
假设我们最近将页面重命名
foo.html
为bar.html
,现在想提供旧的URL以实现向后兼容。但是,我们希望旧URL的用户甚至无法识别页面已被重命名-也就是说,我们不希望在其浏览器中更改地址。
我们通过以下规则在内部将旧网址重写为新网址:
RewriteEngine on RewriteRule "^/foo\.html$" "/bar.html" [PT]
再次假设我们最近将页面重命名
foo.html
为bar.html
,现在想提供旧的URL以实现向后兼容。但是这一次,我们希望旧URL的用户得到新的提示,即他们的浏览器的“位置”字段也应更改。
我们强制HTTP重定向到新URL,从而导致浏览器发生更改,因此用户可以查看:
RewriteEngine on RewriteRule "^/foo\.html$" "bar.html" [R]
与上面的内部示例相比,在此示例中,我们可以简单地使用Redirect指令。在较早的示例中使用了mod_rewrite,以便从客户端隐藏重定向:
Redirect "/foo.html" "/bar.html"
如果资源已移至另一台服务器,则您可能希望在人们更新其书签的同时,URL在旧服务器上继续工作一段时间。
您可以mod_rewrite
用来将这些URL重定向到新服务器,但也可以考虑使用Redirect或RedirectMatch指令。
#With mod_rewrite RewriteEngine on RewriteRule "^/docs/(.+)" "http://new.example.com/docs/$1" [R,L]
#With RedirectMatch RedirectMatch "^/docs/(.*)" "http://new.example.com/docs/$1"
#With Redirect Redirect "/docs/" "http://new.example.com/docs/"
我们如何无缝地将静态页面
foo.html
转换为动态变体
foo.cgi
,即浏览器/用户无需通知。
我们只是将URL重写为CGI脚本,然后将处理程序强制为cgi-script,以便将其作为CGI程序执行。这样,对/~quux/foo.html
内部的请求会导致的调用
/~quux/foo.cgi
。
RewriteEngine on RewriteBase "/~quux/" RewriteRule "^foo\.html$" "foo.cgi" [H=cgi-script]
在迁移document.YYYY
到document.XXXX
(例如将一堆.html
文件转换为)后,如何使URL向后兼容(实际上仍然存在).php
?
我们将该名称重写为其基名称,并测试是否存在新扩展名。如果存在,则使用该名称,否则将URL重写为其原始状态。
# backward compatibility ruleset for # rewriting document.html to document.php # when and only when document.php exists <Directory "/var/www/htdocs"> RewriteEngine on RewriteBase "/var/www/htdocs" RewriteCond "$1.php" -f RewriteCond "$1.html" !-f RewriteRule "^(.*).html$" "$1.php" </Directory>
本示例通过利用规则集的执行顺序,使用了mod_rewrite经常被忽略的功能。特别是,mod_rewrite在评估RewriteCond指令之前先评估RewriteRule的左侧。因此,在评估RewriteCond指令时已经定义了$ 1。这使我们能够使用相同的基本文件名测试原始文件(document.html
)和目标document.php
文件()的存在。
此规则集旨在用于每个目录的上下文(在<Directory>块中或.htaccess文件中),以便
-f
检查使用正确的目录路径。您可能需要设置一个RewriteBase
指令来指定您正在使用的目录库。
解决此问题的最佳方法完全不涉及mod_rewrite,而是Redirect
将虚拟主机中的指令用于非规范主机名。
<VirtualHost *:80> ServerName undesired.example.com ServerAlias example.com notthis.example.com Redirect "/" "http://www.example.com/" </VirtualHost> <VirtualHost *:80> ServerName www.example.com </VirtualHost>
您也可以使用<If>
指令完成此操作
:
<If "%{HTTP_HOST} != 'www.example.com'"> Redirect "/" "http://www.example.com/" </If>
或者,例如,要将您的网站的一部分重定向到HTTPS,您可以执行以下操作:
<If "%{SERVER_PROTOCOL} != 'HTTPS'"> Redirect "/admin/" "https://www.example.com/admin/" </If>
如果出于某种原因仍要使用mod_rewrite
-例如,如果需要此功能以使用更大的RewriteRules集,则可以使用以下配方之一。
对于运行在80以外的端口上的站点:
RewriteCond "%{HTTP_HOST}" "!^www\.example\.com" [NC] RewriteCond "%{HTTP_HOST}" "!^$" RewriteCond "%{SERVER_PORT}" "!^80$" RewriteRule "^/?(.*)" "http://www.example.com:%{SERVER_PORT}/$1" [L,R,NE]
对于在端口80上运行的站点
RewriteCond "%{HTTP_HOST}" "!^www\.example\.com" [NC] RewriteCond "%{HTTP_HOST}" "!^$" RewriteRule "^/?(.*)" "http://www.example.com/$1" [L,R,NE]
如果您想为所有域名一般做到这一点-那就是,如果你想重定向example.com到 www.example.com的所有可能的值 example.com,你可以使用下面的方法:
RewriteCond "%{HTTP_HOST}" "!^www\." [NC] RewriteCond "%{HTTP_HOST}" "!^$" RewriteRule "^/?(.*)" "http://www.%{HTTP_HOST}/$1" [L,R,NE]
这些规则集将在您的主服务器配置文件中或在服务器的.htaccess
放置文件中工作DocumentRoot
。
特定资源可能存在于多个位置之一,我们希望在请求资源时在这些位置中查找该资源。也许我们最近已经重新排列了目录结构,将内容分为几个位置。
以下规则集在两个目录中搜索以找到资源,并且,如果在两个位置均未找到资源,则将尝试仅在请求的位置之外提供资源。
RewriteEngine on # first try to find it in dir1/... # ...and if found stop and be happy: RewriteCond "%{DOCUMENT_ROOT}/dir1/%{REQUEST_URI}" -f RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/dir1/$1" [L] # second try to find it in dir2/... # ...and if found stop and be happy: RewriteCond "%{DOCUMENT_ROOT}/dir2/%{REQUEST_URI}" -f RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/dir2/$1" [L] # else go on for other Alias or ScriptAlias directives, # etc. RewriteRule "^" "-" [PT]
我们的网站有很多镜像,并希望将人们重定向到他们所所在的国家/地区。
查看发出请求的客户的主机名,我们确定他们来自哪个国家。如果我们无法在其IP地址上进行查找,我们将退回到默认服务器。
我们将使用一条RewriteMap
指令来构建我们希望使用的服务器列表。
HostnameLookups on RewriteEngine on RewriteMap multiplex "txt:/path/to/map.mirrors" RewriteCond "%{REMOTE_HOST}" "([a-z]+)$" [NC] RewriteRule "^/(.*)$" "${multiplex:%1|http://www.example.com/}$1" [R,L]
## map.mirrors -- Multiplexing Map
de http://www.example.de/
uk http://www.example.uk/
com http://www.example.com/
##EOF##
HostNameLookups
set on
,这可能会严重影响性能。该RewriteCond
指令捕获请求客户端的主机名的最后一部分-国家/地区代码-并且以下RewriteRule使用该值在映射文件中查找适当的镜像主机。
我们希望基于请求内容的浏览器或用户代理提供不同的内容。
我们必须根据HTTP标头“ User-Agent”确定要提供的内容。以下配置执行以下操作:如果HTTP标头“ User-Agent”包含“ Mozilla / 3”,则页面将foo.html
被重写到foo.NS.html
该位置,并且重写将停止。如果浏览器是版本1或2的“ Lynx”或“ Mozilla”,则URL变为foo.20.html
。所有其他浏览器都会收到page foo.32.html
。这是通过以下规则集完成的:
RewriteCond "%{HTTP_USER_AGENT}" "^Mozilla/3.*" RewriteRule "^foo\.html$" "foo.NS.html" [L] RewriteCond "%{HTTP_USER_AGENT}" "^Lynx/" [OR] RewriteCond "%{HTTP_USER_AGENT}" "^Mozilla/[12]" RewriteRule "^foo\.html$" "foo.20.html" [L] RewriteRule "^foo\.html$" "foo.32.html" [L]
在某些Web服务器上,一个资源有多个URL。通常,存在规范的URL(实际使用和分发的URL)以及只是快捷方式,内部的URL等。无论用户随请求提供了哪个URL,最终他们应该在浏览器地址栏中看到规范的URL。
我们对所有非规范URL进行外部HTTP重定向,以在浏览器的位置视图以及所有后续请求中修复它们。在这个例子中的规则集下面我们替换/puppies
,并/canines
通过规范/dogs
。
RewriteRule "^/(puppies|canines)/(.*)" "/dogs/$2" [R]
RedirectMatch "^/(puppies|canines)/(.*)" "/dogs/$2"
DocumentRoot
通常DocumentRoot
,网络服务器的都直接与URL“ /
”相关。但是通常这些数据并不是真正的最高优先级。例如,您可能希望访问者在首次进入站点时进入特定的子目录/about/
。这可以使用以下规则集来完成:
我们将网址重定向/
到
/about/
:
RewriteEngine on RewriteRule "^/$" "/about/" [R]
请注意,这也可以使用RedirectMatch
指令进行处理:
RedirectMatch "^/$" "http://example.com/about/"
另请注意,该示例仅重写根URL。也就是说,它会重写的请求http://example.com/
,而不是的请求http://example.com/page.html
。如果实际上已经更改了文档根目录-也就是说,如果实际上所有内容都位于该子目录中,则最好只更改DocumentRoot
指令或将所有内容上移到一个目录中,而不是重写URL。
从2.2.16版开始,您应该FallbackResource
为此使用指令:
<Directory "/var/www/my_blog"> FallbackResource "index.php" </Directory>
但是,在Apache的早期版本中,或者如果您的需求比这更复杂,则可以使用以下重写集的变体来完成同一件事:
<Directory "/var/www/my_blog"> RewriteBase "/my_blog" RewriteCond "/var/www/my_blog/%{REQUEST_FILENAME}" !-f RewriteCond "/var/www/my_blog/%{REQUEST_FILENAME}" !-d RewriteRule "^" "index.php" [PT] </Directory>
另一方面,如果您希望将请求的URI作为查询字符串参数传递给index.php,则可以将该RewriteRule替换为:
RewriteRule "(.*)" "index.php?$1" [PT,QSA]
请注意,这些规则集可以在.htaccess
文件以及<Directory>块中使用。
本节中的许多解决方案都将使用相同的条件,这会将匹配的值保留在%2反向引用中。%1是查询字符串的开头(取决于兴趣关键字),而%3是其余部分。对于灵活性和避免替换中使用双'&&'而言,此条件有点复杂。
# Remove mykey=??? RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" RewriteRule "(.*)" "$1?%1%3"
# Copy from query string to PATH_INFO RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" RewriteRule "(.*)" "$1/products/%2/?" [PT]
# Capture the value of mykey in the query string RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" RewriteCond "%2" !=not-so-secret-value RewriteRule "(.*)" - [F]
# The desired URL might be /products/kitchen-sink, and the script expects # /path?products=kitchen-sink. RewriteRule "^/?path/([^/]+)/([^/]+)" "/path?$1=$2" [PT]