Apache rewrite配置注意事项

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 as Alias, Redirect, or ScriptAlias, 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 -Indexes

    RewriteCond %{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 -Indexes

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index\.php?/$1 [QSA,PT,L,NS]
</IfModule>

你可能感兴趣的:(rewrite)