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>