Apache模块 mod_rewrite
说明 状态 模块名 源文件 兼容性
概述
| 一个基于一定规则的实时重写URL请求的引擎 |
| 扩展(E) |
| rewrite_module |
| mod_rewrite.c |
| 仅在 Apache 1.3 及以后的版本中可用 |
此模块提供了一个基于正则表达式分析器的重写引擎来实时重写URL请求。它支持每个完整规则可以拥有不限数量的子规则以及附加条件规则的灵活而且强 大的URL操作机制。此URL操作可以依赖于各种测试,比如服务器变量、环境变量、HTTP头、时间标记,甚至各种格式的用于匹配URL组成部分的查找数 据库。
此模块可以操作URL的所有部分(包括路径信息部分),在服务器级的(httpd.conf)和目录级的(.htaccess)配置都有效,还可以生成最终请求字符串。此重写操作的结果可以是内部子处理,也可以是外部请求的转向,甚至还可以是内部代理处理。
但是,所有这些功能和灵活性带来一个问题,那就是复杂性,因此,不要指望一天之内就能看懂整个模块。
注意:根据你的服务器配置,有可能必须对这里的例子作些小修改,比如,在额外启用mod_alias和mod_userdir的情况下要增加[PT]标志,或者为了适应目录级(.htaccess)的配置而将针对服务器级的规则集进行重写。对一个特定的规则集应该先透彻理解然后再考虑应用,这样才能避免出现问题。
规范化URL 描述: 在有些web服务器上,一个资源会拥有多个URL。在实际应用和发布中应该使用的是规范的URL,其他的则是简写或者只在内部使用。无论用户在请求中使用什么形式的URL,最终看见的都应该是规范的URL。 解决方案: 对所有不规范的URL执行一个外部HTTP重定向,以改变它在浏览器地址栏中的显示及其后继请求。下例中的规则集用规范的/u/user替换/~user,并修正了/u/user所遗漏的后缀斜杠。
RewriteRule ^/~([^/]+)/?(.*) /u/$1/$2 [R] RewriteRule ^/([uge])/([^/]+)$ /$1/$2/ [R]
规范化主机名 描述: 这个规则的目的是强制使用特定的主机名以代替其他名字。比如,你想强制使用www.example.com代替example.com,就可以在以下方案的基础上进行修改: 解决方案:
对运行在非80端口的站点
RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] RewriteCond %{HTTP_HOST} !^$ RewriteCond %{SERVER_PORT} !^80$ RewriteRule ^/(.*) http://fully.qualified.domain.name:%{SERVER_PORT}/$1 [L,R]
对运行在80端口的站点
RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC] RewriteCond %{HTTP_HOST} !^$ RewriteRule ^/(.*) http://fully.qualified.domain.name/$1 [L,R]
移动过的DocumentRoot 描述: 通常,web服务器的DocumentRoot直接对应于URL"/",但是它常常不是处于最高的一级。比如,你希望访问者在进入网站时首先进入/about/目录。可以使用下面给出的规则集。 解决方案: 只需将"/"重定向到"/about/"即可:
RewriteEngine on RewriteRule ^/$ /about/ [R]
也可以使用RedirectMatch指令解决问题:
RedirectMatch ^/$ http://example.com/e/www/
结尾斜杠问题 描述: 每个网管对引用目录的结尾斜杠问题都有一本苦经,如果遗漏了,服务器会产生一个错误,因为如果请求是"/~quux/foo"而不是"/~quux/foo/",服务器就会去找一个叫foo的文件,而它是一个目录,所以就报错了。通常,可以使用这个/~quux/image.gif的请求!所以,应该这样写:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo$ foo/ [R]
又懒又疯狂的做法是把这些写入其宿主目录中的顶级.htaccess中:
RewriteEngine on RewriteBase /~quux/ RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^(.+[^/])$ $1/ [R]
但是这样一来会增加处理上的开销。
将用户主目录移动到不同web服务器 描述: 通常,许多网管在建立一个新的web服务器时,都会有这样的要求:重定向一个web服务器上的所有用户主目录到另一个web服务器。 解决方案: 很简单,在老的web服务器上重定向所有的"/~user/anypath"到"http://newserver/~user/anypath":
RewriteEngine on RewriteRule ^/~(.+) http://newserver/~$1 [R,L]
在多个目录中搜索页面 描述: 有时会有必要使web服务器在多个目录中搜索页面,对此,MultiViews或者其他技术无能为力。 解决方案: 编制一个明确的规则集以搜索目录中的文件:
RewriteEngine on # 首先尝试在 dir1 中寻找,找到即停 RewriteCond /your/docroot/dir1/%{REQUEST_FILENAME} -f RewriteRule ^(.+) /your/docroot/dir1/$1 [L] # 然后尝试在 dir2 中寻找,找到即停 RewriteCond /your/docroot/dir2/%{REQUEST_FILENAME} -f RewriteRule ^(.+) /your/docroot/dir2/$1 [L] # 再找不到就继续寻找其他的 Alias 或 ScriptAlias 目录 RewriteRule ^(.+) - [PT]
按照URL的片段设置环境变量 描述: 希望保持请求之间的状态信息,又不希望使用CGI来包装所有页面,只是通过分离URL中的有用信息来做到。 解决方案: 可以用一个规则集来分离出状态信息,并设置环境变量以备此后用于XSSI或CGI。这样,一个"/foo/S=java/bar/"的URL会被解析为"/foo/bar/",而环境变量STATUS则被设置为"java"。
RewriteEngine on RewriteRule ^(.*)/S=([^/]+)/(.*) $1/$3 [E=STATUS:$2]
虚拟用户主机 描述: 如果需要为用户username支持一个www.username.host.domain.com的主页,但不在此机器上建虚拟主机,而是仅用在此机器上增加一个DNS A记录的方法实现。 解决方案: 仅能对包含"Host: "头的HTTP/1.1请求实现。可以使用以下规则集内部地将upload/200904241424198373.gif" alt="top" />
为外来访问者重定向用户主目录 描述: 对不是来自本地域ourdomain.com的外来访问者的请求,重定向其用户主目录URL到另一个web服务器www.somewhere.com,有时这种做法也会用在虚拟主机的配置段中。 解决方案: 只须一个重写条件:
RewriteEngine on RewriteCond %{REMOTE_HOST} !^.+\.ourdomain\.com$ RewriteRule ^(/~.+) upload/200904241424204142.gif" alt="top" />
重定向锚 描述: 默认情况下,重定向到一个HTML锚是不可行的,因为'#'会被转义为'%23'。This, in turn, breaks the redirection. 解决方案: 在RewriteRule指令中使用[NE]标志(不转义)。
对YYYY转变为XXXX的向前兼容 描述: 在转变了大批document.YYYY文件为document.XXXX后(比如.html→.phtml),如何保持URL的向前兼容(仍然虚拟地存在)? 解决方案: 只须按基准文件名重写,并测试带有新的扩展名的文件是否存在,如果存在则用新的,否则仍然用原来的。
# 将document.html重写为document.phtml的向后兼容的规则集 # 当且仅当document.phtml存在且document.html不存在的时候 RewriteEngine on RewriteBase /~quux/ # 剪切并记住basename RewriteRule ^(.*)\.html$ $1 [C,E=WasHTML:yes] # 如果存在的话就重写为document.phtml RewriteCond %{REQUEST_FILENAME}.phtml -f RewriteRule ^(.*)$ $1.phtml [S=1] # 否则返回先前的basename RewriteCond %{ENV:WasHTML} ^yes$ RewriteRule ^(.*)$ $1.html
内容处理 从旧到新(内部) 描述: 假定已经把文件foo.html改名为bar.html,需要对老的URL向后兼容,即让用户仍然可以使用老的URL,而感觉不到文件被改名了。 解决方案: 通过以下规则内部地将老的URL重写为新的:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html
从旧到新(外部) 描述: 仍然假定已经把文件foo.html改名为bar.html,需要对老的URL向后兼容,但是要让用户得到文件被改名的暗示,即浏览器的地址栏中显示的是新的URL。 解决方案: 作一个HTTP的强制重定向以改变浏览器和用户界面上的显示:RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html [R]
从静态到动态 描述: 如何无缝转换静态页面foo.html为动态的foo.cgi,而不为浏览器/用户所察觉。 解决方案: 只须重写此URL为CGI-script,并强制作为CGI-script运行的MIME类型。比如对/~quux/foo.html的请求会执行/~quux/foo.cgi 。RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ foo.cgi [T=application/x-httpd-cgi]
访问控制 阻止Robot 描述: 如何阻止一个完全匿名的Robot取得特定网络区域的页面?/robots.txt文件可以包含若干"Robot排除协议"行,但不足以阻止此类Robot。 解决方案: 可以用一个规则集以拒绝对网络区域/~quux/foo/arc/(对一个很深的目录区域进行列表可能会使服务器产生很大的负载)的访问。还必须确保仅阻止特定的Robot,也就是说,仅仅阻止Robot访问主机是不够的(这样会同时阻止用户访问该主机)。为此,就需要对HTTP头的User-Agent信息作匹配。
RewriteCond %{HTTP_USER_AGENT} ^NameOfBadRobot.* RewriteCond %{REMOTE_ADDR} ^123\.45\.67\.[8-9]$ RewriteRule ^/~quux/foo/arc/.+ - [F]
阻止内嵌的图片 描述: 假设http://www.quux-corp.de/~quux/有一些内嵌GIF图片的页面,这些图片很好,所以就有人盗链到他们自己的页面中了。由于这样徒然增加了我们服务器的流量,因此,我们不愿意这种事情发生。 解决方案: 虽然,我们不能100%地保护这些图片不被写入别人的页面,但至少可以对发出HTTP Referer头的浏览器加以限制。RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://www.quux-corp.de/~quux/.*$ [NC] RewriteRule .*\.gif$ - [F]
RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !.*/foo-with-gif\.html$ RewriteRule ^inlined-in-foo\.gif$ - [F]
拒绝代理 描述: 如何拒绝某个主机或者来自特定主机的用户使用Apache代理? 解决方案: 首先,要确保在配置文件中其它 外部重写引擎 描述: 如何解决似乎无法用mod_rewrite解决的FOO/BAR/QUUX/之类的问题? 解决方案: 可以使用一个与RewriteMap功能相同的外部RewriteMap程序,一旦它在Apache启动时被执行,则从STDIN接收被请求的URL ,并将处理过(通常是重写过的)的URL(以相同顺序)在STDOUT输出。
RewriteEngine on RewriteMap quux-map prg:/path/to/map.quux.pl RewriteRule ^/~quux/(.*)$ /~quux/${quux-map:$1}
#!/path/to/perl # 禁止使用会导致Apache陷入死循环的I/O缓冲 $| = 1; # 从stdin读取URL(每行一个),并在stdout输出替换URL while (<>) { s|^foo/|bar/|; print $_; }
这是只是一个简单的示例,只是把所有的/~quux/foo/...重写为/~quux/bar/...而已。但事实上,可以把它修改成任何你想要的输出。但是要注意,虽然一般用户都可以使用,可是只有系统管理员才可以定义这样的映射。