mod_rewrite的坑太多。php
1、没法取得$_SERVER[‘SCRIPT_NAME’]html
当添加rewrite后,写下如此的规则web
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule ^(.*)$ /index.php/$1 [QSA,L]正则表达式
打开phpinfo,发现$_SERVER[‘SCRIPT_NAME’]为”no value”,这在须要判断script_name的单入口mvc中多是致命的,由于它可能须要用来取得rewrite过的路径。解决方法是在RewriteRule规则修饰添加PT标记。apache
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.mvc
强制rewrite的URI结果传递给映射引擎(mapping engine)处理其它URI与filename的解析,好比别名Alias或转向Redirect。app
那么passthroungh究竟是如何工做的呢?webapp
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没必要)。
三、.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>