mod_rewrite的坑太多。
1、无法取得$_SERVER[‘SCRIPT_NAME’]
当添加rewrite后,写下如此的规则
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule ^(.*)$ /index.php/$1 [QSA,L]
打开phpinfo,发现$_SERVER[‘SCRIPT_NAME’]为”no value”,这在需要判断script_name的单入口mvc中可能是致命的,因为它可能需要用来取得rewrite过的路径。解决方法是在RewriteRule规则修饰添加PT标记。
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule ^(.*)$ /index.php/$1 [QSA,PT,L]
根据rewrite手册的描述,passthrough|PT标记为:
Forces the resulting URI to be passed back to the URL mapping engine for processing of other URI-to-filename translators, such as Alias or Redirect.
强制rewrite的URI结果传递给映射引擎(mapping engine)处理其它URI与filename的解析,比如别名Alias或转向Redirect。
那么passthroungh到底是如何工作的呢?
The target (or substitution string) in a RewriteRule is assumed to be a file path, by default. The use of the [PT] flag causes it to be treated as a URI instead. That is to say, the use of the [PT] flag causes the result of the
RewriteRule
to be passed back through URL mapping, so that location-based mappings, such asAlias
,Redirect
, orScriptAlias
, for example, might have a chance to take effect.rewrite后的结果默认会被假定为一个文件路径,而[PT]标记的使用则使字串被当作一个URI(url资源)来对待,也就是说,[PT]标记的使用导致RewriteRule处理的结果又被传回到了URL mapping,这样方便基于文件路径的映射计算。举例:
Alias "/icons" "/usr/local/apache/icons"
RewriteRule "/pics/(.+)\.jpg$" "/icons/$1.gif" [PT]
此例中,省略[PT]标记的结果就是上面的Alias(别名)替换规则被忽略,造成返回“File not found”。因为在路径/icons中无法找到*.gif,它实际指向了/user/local/apache/icons/*.gif。
实际,RewriteRull加[PT]标记的流程就是:
uri –> filepath –> [pt] –>uri(replace)->filepath
本来我们将用户请求的uri地址映射到服务器系统中实际的文件路径,找到脚本来处理,但因为pt标记,它还需要将rewrite的结果再转回一串uri资源,查找替换回真正的路径地址。
实际最后返回的还是一个路径,只不过多了一层映射关系。
PT 标记隐含带有 L 标记的作用: 将停止rewrite,以便传递到下一阶段的处理请求。
值得一提的是PT在每个目录环境是暗中发挥作用的,例如 <Directory> 块中或 .htaccess 文件中。避免这一作用的唯一办法就是将匹配rewrite为 -。因此,在.htaccess中的RewriteRule的修饰,在有PT的情况下,不必再加L标记。
说了这么多,还没有交待PT标记与SCRIPT_NAME的关系。实际上这里也没交待,唯一能猜测的就是,SCRIPT_NAME值就是在这里被加上去的。
2、RewriteCond在conf与.htaccess中的不同。
具体的错误参考这篇博客。
在httpd.conf中,如果RewriteConf不添加%{DOCUMENT_ROOT}的地址,input出现的将是相对地址"/a./b"映射后的rewrite,将是'/a/b' -> 'index.php/a/b'。
[rid#9e8030/initial] (4) RewriteCond: input='/a/b' pattern='!-s' => matched
加上后才会出现绝对地址:
RewriteCond: input='D:/Tomcat/webapps/ROOT/a/b' pattern='!-s' => not-matched
这是因为在文件系统(.htaccess)中,默认就是:
<IfModule rewrite_module>
RewriteBase /
</IfModule>
因此, .htaccess中的rewrite是相对于.htaceess所在路径的。而在conf中是不能使用RewriteBase标记当前根目录的。
所以,在conf中的rewrite的正确写法应该是:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-s
RewriteRule ^/(.*)$ /index.php/$1 [QAS,L]
注意,index.php前面需要加前导/,代表的是DOCUEMENT_ROOT目录下的index.php文件,不加会丢失DOCUMENT_ROOT(在.htaccess不必)。
3、.htaccess的配置事项
1)、重写规则的说明。
RewriteRule指令是真正重写的工作区间。此指令可以多次使用,每条指令定义一个单一的重写规则。其中定义这些规则的顺序非常重要 — — 这是将在运行时应用的顺序。
Pattern 是 perl 兼容的正则表达式。第一次运用rewrite规则,它对照 (已解码%) 请求的URL-path,或在per-directory (见下文),URL 路径相对于当前目录环境 。随后的模式对照上次匹配重写规则的输出。
2)、如何匹配
在VirtualHost(虚拟主机)方面,Pattern,将是匹配 完主机名和端口剩下的URL部份,在查询字符串(query string)之前匹配 (例如"/ app1/index.html")。
在Directory和 htaccess 环境,在删除能够引导服务器至当前的RewriteRule的前缀后,Pattern将优先匹配为filesystem路径 (例如"app1/index.html"或"index.html"根据指令在哪里定义) 。
如果你想匹配的机名、 端口或查询字符串,分别 使用%{HTTP_HOST},{SERVER_PORT} %或 %{QUERY_STRING} 等RewriteCond。
3、目录中的重写
可以一些额外的工作在.htaccess 文件中和 <Directory>块中使用rewrite引擎。
在这样的环境中使用rewrite引擎,您需要设置"RewriteEngine On",而且必须启用"Options FollowSymLinks"。如果管理员在用户目录中禁用了FollowSymLinks,则不能使用rewrite引擎。这是出于安全的需要。
当在 .htaccess文件使用rewrite引擎时,单目录前缀(对于指定的目录都是相同的)会被RewriteRul匹配模式自动移除,并且会被规则集自动地替换为相对的前缀(不能为前导斜线/或通道名protocal name,例如http://开头)。有关相对替换前缀的详细介绍参见RewriteBase
指令。
如果需要在单目录中(.htaccess)匹配完整的URL路径,请在RewriteCond使用%{REQUEST_URI}
变量。
移除的前缀通常以斜杠/结尾,意为匹配结果将永远不会出现前导斜线/开头,因此,^/的 Pattern(匹配模式)将不会找到匹配。(注:通常在.htaccess中的匹配路径为^(.*)$)。举例,
如果请求url为http://www.xxx.com/a/b/c,那么,在a目录下建立的.htaccess,匹配得到的uri资源对应的将是:
b/c
而非
/b/c
在RewriteDebug中看到的替换结果将是:
rewrite 'b/c' -> 'index.php/b/c'
虽然重写规则语法规则允许使用在<Location>
and <Files>块(包括相应的正则表达式),但这是不必要,也是永远不支持的做法,这样产生的一个可能性就是会破坏上下文已经建立的其它重写规则。
4、一点不解
在我的
Apache/2.2.25 (Win32) mod_fcgid/2.3.6版本中,.htaccess的设置没问题
AcceptPathInfo On
<IfModule rewrite_module>
RewriteEngine On
RewriteBase /
Options FollowSymlinks ExecCGI -IndexesRewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index\.php/$1 [QSA,PT,L,NS]
</IfModule>
但始终不能rewrite成功,相同的配置下Apache/2.2.29 fpm-cig却可以。是不是因为VirtualHost的缘故?!
最后只能通过?号参数的形式简接rewrite:
<IfModule rewrite_module>
RewriteEngine On
RewriteBase /
Options FollowSymlinks ExecCGI -IndexesRewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index\.php?/$1 [QSA,PT,L,NS]
</IfModule>