2011-04-07 132 views
5

今天早些時候,我正在幫助有人使用.htaccess用例,並came up with a solution有效,但不能自己弄明白!這爲什麼會導致無限的請求循環?

他希望能夠:

  • 瀏覽index.php?id=3&cat=5
  • 看到地址欄閱讀index/3/5/
  • 讓內容從index.php?id=3&cat=5

擔任最後兩個步驟都相當典型的(通常來自用戶首先輸入index/3/5),但第一步是必需的,因爲他仍然有他網站中的一些舊格式鏈接,無論出於何種原因,都無法改變它們。所以他需要支持這兩個的URL格式,並讓用戶總是最終看到美化的。

後多少-ING回回-ING,我們提出了以下.htaccess文件:

RewriteEngine on 

# Prevents browser looping, which does seem 
# to occur in some specific scenarios. Can't 
# explain the mechanics of this problem in 
# detail, but there we go. 
RewriteCond %{ENV:REDIRECT_STATUS} 200 
RewriteRule .* - [L] 

# Hard-rewrite ("[R]") to "friendly" URL. 
# Needs RewriteCond to match original querystring. 
# Uses "?" in target to remove original querystring, 
# and "%n" backrefs to move its components. 
# Target must be a full path as it's a hard-rewrite. 
RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$ 
RewriteRule ^index\.php$ http://example.com/index/%1/%2/? [L,R] 

# Soft-rewrite from "friendly" URL to "real" URL. 
# Transparent to browser. 
RewriteRule ^index/(\d+)/(\d+)/$ /index.php?id=$1&cat=$2 

雖然它似乎是一個有些奇怪的使用情況(」 爲什麼不使用正確的鏈接在第一個地方?「,你可能會問),隨它去吧。無論原始要求如何,這都是場景,它讓我發瘋。

沒有第一條規則,客戶端進入一個請求循環,每次嘗試重複GET /index/X/Y/並得到302。對REDIRECT_STATUS的檢查使一切順利進行。但我會認爲,在最終規則之後,不會再有更多的規則可供選擇,客戶不會再提出任何要求(請注意,沒有[R]),並且所有的事情都會變成肉汁。

所以...爲什麼這會導致請求循環時,我拿出第一條規則?

+1

在我看來,並不奇怪的用法 – Cyclone 2011-04-07 00:16:10

回答

3

沒有能夠與您的設置鼓搗,我不能肯定地說,但我相信這個問題是由於mod_rewrite的以下比較神祕的功能:

當你操縱URL /文件名在每個目錄的上下文中,mod_rewrite會首先將文件名重新寫回到其相應的URL(通常不可能,但請參閱下面的RewriteBase指令以實現此目的),然後用新URL啓動一個新的內部子請求。這將重新開始處理API階段。

(來源:mod_rewrite technical documentation,我高度推薦閱讀本)

換句話說,當你在一個.htaccess文件使用RewriteRule,它可能是新的,重寫URL映射到一個完全不同的在這種情況下,原始目錄中的.htaccess文件將不再適用。因此,只要.htaccess文件中的RewriteRule與請求匹配,Apache就必須重新啓動處理與修改後的URL。這意味着,除其他外,每RewriteRule被再次檢查。

就你而言,會發生什麼事是你從瀏覽器訪問/index/X/Y/。您的.htaccess文件中的最後一條規則會觸發,並將其重寫爲/index.php?id=X&cat=Y,因此Apache必須創建一個新的內部子請求,其URL爲/index.php?id=X&cat=Y。這與您之前的外部重定向規則相匹配,因此Apache會將302響應發送回瀏覽器以將其重定向到/index/X/Y/。但請記住,瀏覽器從來沒有看到內部的子請求;據它所知,它已經在/index/X/Y/上。所以它看起來像你從/index/X/Y/重定向到同一個URL,觸發了一個無限循環。

除了性能問題,這可能是更好的理由之一,你應該儘可能避免在.htaccess文件中重寫規則。如果將這些規則移至主服務器配置,則不會出現此問題,因爲規則上的匹配不會觸發內部子請求。如果你沒有訪問主服務器配置文件,你可以解決它的一種方法(編輯:或者我以爲,雖然它似乎沒有工作 - 見評論)是通過添加[NS](no子請求)標誌,以你的外部重定向規則,

RewriteRule ^index\.php$ http://example.com/index/%1/%2/? [L,R,NS] 

一旦你這樣做,你應該不再需要該檢查REDIRECT_STATUS的第一條規則。

+0

你是,只有一天,我的英雄和救世主。 – 2011-04-07 08:42:19

+0

(雖然在第一次檢查時,任何一條規則上的「NS」似乎都沒有什麼區別) – 2011-04-07 08:44:52

+0

不管怎麼樣,我會接受它......如果沒有別的,你提供了關於最可能的原因的一個很好的解釋,這是我以後的事。謝謝 – 2011-04-07 22:30:30

0

下面的解決方案爲我工作。

RewriteEngine on 
RewriteBase/

#rule1 
#Guard condition: only if the original client request was for index.php 
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.php [NC] 
RewriteCond %{QUERY_STRING} ^id=(\d+)&cat=(\d+)$ [NC] 
RewriteRule . /index/%1/%2/? [L,R] 

#rule 2 
RewriteRule ^index/(\d+)/(\d+)/$ /index.php?id=$1&cat=$2 [L,NC] 

以下是我認爲正在發生的

從你上面

  1. 瀏覽引用到index.php步驟?ID = 3 &貓= 5
  2. 見位置欄閱讀指數/ 3/5/
  3. 是否有從index.php提供的內容?id = 3 & cat = 5

在步驟1中,規則1場比賽和重定向到位置欄,並滿足步驟2.

在步驟3中,規則2現在匹配並且重寫到index.php。

由於David陳述的原因,規則重新運行,但由於THE_REQUEST一旦設置爲原始請求就不可變,它仍然包含/index/3/5因此規則1不匹配。

規則2不匹配,index.php的結果被提供。

大多數其他變量是可變的,例如, REQUEST_URI。它們在規則處理期間的修改以及模式匹配的錯誤期望與原始請求相反是無限循環的常見原因。

它的感覺相當深奧的時候,但是我肯定有其複雜的邏輯原因:-)

編輯

當然有兩種截然不同的請求

有2個客戶端請求,來自Step1的原始請求和來自步驟2的外部重定向的請求。

我上面提到的是,當第二個請求中的規則2匹配時,它被重寫爲/index.php並導致內部重定向。這會強制/目錄的.htaccess文件再次加載(它可能很容易成爲具有不同.htaccess規則的另一個目錄),並再次運行所有規則。

所以......當我拿出第一條規則時,爲什麼會導致請求循環?

當規則重新運行時,由於Rule2的重寫,第一條規則現在意外地匹配,並進行重定向,導致無限循環。

大衛的回答確實包含了大部分這些信息,我的意思是「出於大衛說的原因」。

但是,這裏的要點是,您確實需要額外的條件,無論是您的條件,停止進一步的規則處理內部重定向,或我的,防止規則1匹配,是必要的,以防止無限循環。

+0

當然有兩個不同的請求 – 2012-01-08 17:48:04

+0

@LightnessRacesinOrbit see edit – 2012-01-08 21:13:14

相關問題