2011-06-15 12 views
9

我寫了一個Web應用程序,我運行在一個專用的服務器上託管Web應用程序。此Web應用程序的實例可在不同的域中使用,並且每個域都有自己的Web應用程序文件副本,允許根據需要進行定製。如何在使用通配符的Directory指令中使用apache2 mod_rewrite?

我在Debian Squeeze下運行Apache/2.2.16。

我在VirtualHost指令下執行所有配置,不使用.htaccess文件。

爲了簡化apache的配置,我想維持像這樣的單個目錄指令:

<Directory "/srv/www/*/public/"> 
    RewriteEngine on 
    RewriteCond %{REQUEST_FILENAME} !-f 
    RewriteCond %{REQUEST_FILENAME} !-d 
    RewriteCond %{REQUEST_URI} !=/favicon.ico 
    RewriteCond %{REQUEST_URI} !=/robots.txt 
    RewriteRule ^(.+)$ /index.php?q=$1 [L,QSA] 
</Directory> 

然而,重寫規則產生錯誤的結果,因爲在使用通配符指南值,它未能剝離每個目錄前綴。這裏是重寫日誌的輸出:

[rid#b9832078/initial] (3) [perdir /srv/www/*/public/] applying pattern '^(.+)$' to uri '/srv/www/domain1/public/login' 
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-f' => matched 
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-d' => matched 
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/login' pattern='!=/favicon.ico' => matched 
[rid#b9832078/initial] (4) [perdir /srv/www/*/public/] RewriteCond: input='/login' pattern='!=/robots.txt' => matched 
[rid#b9832078/initial] (2) [perdir /srv/www/*/public/] rewrite '/srv/www/domain1/public/login' -> '/index.php?q=/srv/www/domain1/public/login' 
[rid#b9832078/initial] (3) split uri=/index.php?q=/srv/www/domain1/public/login -> uri=/index.php, args=q=/srv/www/domain1/public/login 
[rid#b9832078/initial] (1) [perdir /srv/www/*/public/] internal redirect with /index.php [INTERNAL REDIRECT] 
[rid#b9847440/initial/redir#1] (3) [perdir /srv/www/*/public/] applying pattern '^(.+)$' to uri '/srv/www/domain1/public/index.php' 
[rid#b9847440/initial/redir#1] (4) [perdir /srv/www/*/public/] RewriteCond: input='/srv/www/domain1/public/index.php' pattern='!-f' => not-matched 
[rid#b9847440/initial/redir#1] (1) [perdir /srv/www/*/public/] pass through /srv/www/domain1/public/index.php 

的問題是,在重寫規則「URI」是文件系統路徑,而不是URL路徑,這導致查詢字符串是不正確的:Q =/SRV/WWW /域1 /公/登錄

顯式指定的目錄路徑就像這樣:

<Directory "/srv/www/domain1/public/"> 
    RewriteEngine on 
    RewriteCond %{REQUEST_FILENAME} !-f 
    RewriteCond %{REQUEST_FILENAME} !-d 
    RewriteCond %{REQUEST_URI} !=/favicon.ico 
    RewriteCond %{REQUEST_URI} !=/robots.txt 
    RewriteRule ^(.+)$ /index.php?q=$1 [L,QSA] 
</Directory> 

工作得很好,在這裏被改寫日誌顯示正確的行爲(不同之處是新的輸出冷杉牛逼附加線提供正確的輸入產生正確的查詢字符串重寫的休息:Q =登錄):

[rid#b9868048/initial] (3) [perdir /srv/www/domain1/public/] strip per-dir prefix: /srv/www/domain1/public/login -> login 
[rid#b9868048/initial] (3) [perdir /srv/www/domain1/public/] applying pattern '^(.+)$' to uri 'login' 
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-f' => matched 
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/srv/www/domain1/public/login' pattern='!-d' => matched 
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/login' pattern='!=/favicon.ico' => matched 
[rid#b9868048/initial] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/login' pattern='!=/robots.txt' => matched 
[rid#b9868048/initial] (2) [perdir /srv/www/domain1/public/] rewrite 'login' -> '/index.php?q=login' 
[rid#b9868048/initial] (3) split uri=/index.php?q=login -> uri=/index.php, args=q=login 
[rid#b9868048/initial] (1) [perdir /srv/www/domain1/public/] internal redirect with /index.php [INTERNAL REDIRECT] 
[rid#b987d5f8/initial/redir#1] (3) [perdir /srv/www/domain1/public/] strip per-dir prefix: /srv/www/domain1/public/index.php -> index.php 
[rid#b987d5f8/initial/redir#1] (3) [perdir /srv/www/domain1/public/] applying pattern '^(.+)$' to uri 'index.php' 
[rid#b987d5f8/initial/redir#1] (4) [perdir /srv/www/domain1/public/] RewriteCond: input='/srv/www/domain1/public/index.php' pattern='!-f' => not-matched 
[rid#b987d5f8/initial/redir#1] (1) [perdir /srv/www/domain1/public/] pass through /srv/www/domain1/public/index.php 

我希望我遇到與Apache的錯誤,但如果ISN這種情況,我做錯了什麼?

雖然我很欣賞將方法改爲另一個可行的解決方案的輸入,但我會接受一個答案,以我採取的方法解決它(例如,不使用.htaccess),除非可以顯示此方法不可解。

因此,在通配符目錄中使用時,是否有必須更改爲RewriteCond/Rules?

好奇的一面注意事項:爲了進一步簡化,我使用了一個使用VirtualDocumentRoot的單一VirtualHost - 但這與這個問題在使用'DocumentRoot'並在單個域下測試時複製無關。

編輯

好吧,這基於regilero的答案,在這裏我已經重新出現是什麼 - 移動重寫,按原樣出目錄的結果在輕微的首要問題查詢字符串從「登錄」更改爲「/登錄」,這通過修改RewriteRule爲:RewriteRule ^/(.+)$ /index.php?q=$1 [L,QSA]修復了我以前的「莫名其妙的失敗」評論。

在此之後,所有的靜態文件加載失敗,這裏是顯示這個問題重寫日誌:

[rid#b7bc7fa0/initial] (2) init rewrite engine with requested uri /login 
[rid#b7bc7fa0/initial] (3) applying pattern '^/(.+)$' to uri '/login' 
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!-f' => matched 
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!-d' => matched 
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!=/favicon.ico' => matched 
[rid#b7bc7fa0/initial] (4) RewriteCond: input='/login' pattern='!=/robots.txt' => matched 
[rid#b7bc7fa0/initial] (2) rewrite '/login' -> '/index.php?q=login' 
[rid#b7bc7fa0/initial] (3) split uri=/index.php?q=login -> uri=/index.php, args=q=login 
[rid#b7bc7fa0/initial] (2) local path result: /index.php 
[rid#b7bc7fa0/initial] (2) prefixed with document_root to /srv/www/domain1/public/index.php 
[rid#b7bc7fa0/initial] (1) go-ahead with /srv/www/domain1/public/index.php [OK] 
[rid#b7be6b80/initial] (2) init rewrite engine with requested uri /static/css/common.css 
[rid#b7be6b80/initial] (3) applying pattern '^/(.+)$' to uri '/static/css/common.css' 
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!-f' => matched 
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!-d' => matched 
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!=/favicon.ico' => matched 
[rid#b7be6b80/initial] (4) RewriteCond: input='/static/css/common.css' pattern='!=/robots.txt' => matched 
[rid#b7be6b80/initial] (2) rewrite '/static/css/common.css' -> '/index.php?q=static/css/common.css' 
[rid#b7be6b80/initial] (3) split uri=/index.php?q=static/css/common.css -> uri=/index.php, args=q=static/css/common.css 
[rid#b7be6b80/initial] (2) local path result: /index.php 
[rid#b7be6b80/initial] (2) prefixed with document_root to /srv/www/domain1/public/index.php 
[rid#b7be6b80/initial] (1) go-ahead with /srv/www/domain1/public/index.php [OK] 

但是就像我在我的評論說regilero的回答,這是由前綴解決RewriteCond指令帶有%{DOCUMENT_ROOT}的TestString。但是,使用%{DOCUMENT_ROOT}在使用VirtualDocumentRoot時不起作用。

對於我而言,%{DOCUMENT_ROOT}前綴應該是必需的。

編輯

REQUEST_FILENAME

完整的本地文件系統中的文件路徑或腳本匹配 請求時,如果這已經由服務器在 REQUEST_FILENAME被引用的時間來決定。否則,如在虛擬主機上下文中使用 時,與REQUEST_URI的值相同。

它解釋了需要DOCUMENT_ROOT前綴。

我已經更新了重寫規則這樣:

RewriteCond %{REQUEST_URI} !=/favicon.ico 
RewriteCond %{REQUEST_URI} !=/robots.txt 
RewriteCond %{REQUEST_URI} !^/static/ 
RewriteRule ^/(.+)$ /index.php?q=$1 [PT,L,QSA] 

哪些工作正常(注:PT標誌必須避免過早使用VirutalDocumentRoot當翻譯URL路徑文件系統路徑)。這裏行爲的主要變化是RewriteCond對於進入應用程序的所有入口點都是必需的 - 類似於/ static行。

編輯

這是任何目錄指令超出了我的虛擬主機重寫指令的最終化身:

RewriteEngine on 
RewriteCond %{REQUEST_URI} !^/static/ 
RewriteCond %{REQUEST_URI} !=/favicon.ico 
RewriteCond %{REQUEST_URI} !=/robots.txt 
RewriteRule ^/(.+)$ /index.php?q=$1 [NS,PT,L,QSA] 
RewriteRule ^/$ /index.php [NS,PT,L,QSA] 

我已經添加了NS標誌,以避免額外的內部評估,並添加第二個RewriteRule指令傾向於使用mod_dir和DirectoryIndex。我的應用程序預計根URL沒有q =參數,否則如果應用程序被更新爲接受根URL的空q=參數,則單個RewriteRuleRewriteRule ^/(.*)$ /index.php?q=$1 [NS,PT,L,QSA]就足夠了。我可能會在未來做到這一點。

回答

7

非常好的和詳細的問題。

你肯定已經遇到了一個錯誤,或者至少有一個沒有記錄的rewriteRule域。文件指出:

  • 重寫引擎可以在.htaccess文件和 部分使用,有一些額外的複雜性。
  • 要在此上下文中啓用重寫引擎,您需要設置 「RewriteEngine On」和「Options FollowSymLinks」必須啓用。如果 您的管理員已禁用覆蓋FollowSymLinks的 用戶目錄,則無法使用重寫引擎。出於安全原因,此限制 是必需的。
  • 當使用在.htaccess重寫引擎文件的每目錄 前綴(其總是爲特定的目錄相同的)自動是 用於重寫規則圖案匹配移除並 後自動添加任何相對(未開始與斜線或協議名稱)替換 遇到規則集的結尾。有關更多 信息,請參閱RewriteBase指令有關哪些前綴將添加回相關子程序。

因此,沒有使用通配符的事實<Directory>指令的提及將不能夠剝離每個目錄前綴。和RewriteBase一起玩並不會幫助你,重建最終的Url並不會改變perdir的工作。

但是正如您在開始時可以看到的那樣,「」帶有一些額外的複雜性「句子。 mod-rewrite完成的目錄操作比一般的目錄外重寫規則更慢更復雜。這在documentation中也有說明,主要是因爲perdir strip操作。這意味着你也可以在你的VirtualHost中寫出你的rewriteRule出<Directory>部分。

  • 它會更快
  • 它不會受到此問題的
  • 它可能有一些副作用,如果一些不存在的,文件should'nt在一些其他目錄映射到您的index.php?q=$1規則擊中。但我很確定這不是你的問題。

所以簡單的寫(無通配符目錄):

RewriteEngine on 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d 
RewriteCond %{REQUEST_URI} !=/favicon.ico 
RewriteCond %{REQUEST_URI} !=/robots.txt 
RewriteRule ^(.+)$ /index.php?q=$1 [L,QSA] 

,它應該工作,如果讓這會導致新的問題我知道。

編輯:

好,forogot事實REQUEST_FILENAME是在虛擬主機上下文中定義尚未complelty,它的記錄,它的「正常」,當條件應用於文件搜索的真正路徑上尚未完成,這就是爲什麼您必須添加文檔根目錄。所以實際上你的最終解決方案應該是:

RewriteEngine on 
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f 
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d 
RewriteCond %{REQUEST_URI} !=/favicon.ico 
RewriteCond %{REQUEST_URI} !=/robots.txt 
RewriteRule ^/(.+)$ /index.php?q=$1 [L,QSA] 

我嘗試了第二個,避免DOCUMENT_ROOT,通過使用REQUEST_FILENAME末評估(%{LA-U:REQUEST_FILENAME}包含最終路徑,這是在事實上,在不存在文件的情況下,index.php的完整路徑),但是我得到它的唯一方法是在第二個中添加第二個Rule和Or條件,不那麼簡單,所以第一個解決方案當然更好(KISS )。

RewriteCond %{LA-U:REQUEST_FILENAME} !-f [OR] 
    RewriteCond %{LA-U:REQUEST_FILENAME} !/index.php 
    RewriteCond %{LA-U:REQUEST_FILENAME} !-d 
    RewriteCond %{REQUEST_URI} !=/favicon.ico 
    RewriteCond %{REQUEST_URI} !=/robots.txt 
    RewriteRule ^/(.+)$ /index.php?q=$1 [L,QSA] 

    RewriteCond %{LA-U:REQUEST_FILENAME} /index.php 
    RewriteRule ^/(.+)$ /index.php?q=$1 [L,QSA] 
+0

它沒有,我以前試過,它需要在RewriteCond的前綴%{DOCUMENT_ROOT} - 但即使如此,它莫名其妙地失敗。我將很快重新運行該設置以提供重寫日誌輸出。另外,使用%{DOCUMENT_ROOT}在Apache2.2中無法使用VirtualDocumentRoot - 雖然最近幾天前已經修復了Apache2。3 – chris 2011-06-15 16:21:53

+0

因此,您可以嘗試使用mod_macro來代替VirtualDocumentRoot方法,但在mod_vhost_alias中引入的所有錯誤都不存在於由mod_macro定義的虛擬主機(但您需要每個新用戶的一行conf)。 – regilero 2011-06-15 16:42:07

+0

我已經更新了我的問題,並在Directory指令之外移動了重寫指令的結果。 – chris 2011-06-15 16:53:02