Dot-Htaccess

.htaccess mod_rewrite 未擷取所有 RewriteRules

  • February 25, 2022

有一個 PHP 應用程序,它帶有一個 PHP 路由器作為放置在 index.php 中的所有請求的入口點。我正在嘗試編寫一個 .htaccess 文件來將每個請求轉發到 index.php,API 請求和設計資源除外。所以我試圖獲得以下行為:

  1. example.com/api/v1/*應該服務api_v1.php
  2. example.com/any_path/resource.css=>resource.css如果存在就應該服務(允許有多個擴展;.css只是一個例子)
  3. 服務index.php於不符合上述條件的任何事物

鑑於.htaccess從上到下,從特定條件到一般條件進行評估,並且[L]如果有任何匹配,該標誌將中斷執行,我設法提出以下建議.htaccess

RewriteEngine On

# Prevent 301 redirect with slash when folder exists and does not have slash appended
# This is not a security issue here since a PHP router is used and all the paths are redirected
DirectorySlash Off

#1. Rewrite for API url
RewriteRule ^api/v1/(.*)$ api_v1.php [L,NC]

#2. Rewrite to index.php except for for design/document/favicon/robots files that exists
RewriteCond %{REQUEST_URI} !.(css|js|png|jpg|jpeg|bmp|gif|ttf|eot|svg|woff|woff2|ico|webp|pdf)$
RewriteCond %{REQUEST_URI} !^(robots\.txt|favicon\.ico)$ [OR]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L]

#3. Rewrite enything else
RewriteRule ^(.*)$ index.php [L]

使用上面的程式碼,似乎訪問example.com/api/v1/不執行api_v1.php。相反,它將繼續並執行index.php

example.com/api/v1/僅當我刪除第 8 行之後的所有條件時才有效。

我在這裡做錯了什麼?

似乎訪問 site.com/api/v1/ 不會執行 api_v1.php。相反,它會繼續執行 index.php

是的,因為第一個規則將請求重寫為api_v1.php,這最終index.php被第二個規則重寫(因為php不在第一個條件的排除擴展列表中,第二個條件也成功,因此不處理第三個條件 -見下文)在重寫引擎的第二次傳遞期間。在目錄上下文(即。.htaccess)中,該L標誌不會停止所有處理,它只是停止目前通過重寫引擎。重寫引擎循環,直到 URL 不變地通過。

您可以在 Apache 2.4 上通過在第一條規則上使用END標誌而不是 來解決此問題L,以停止重寫引擎的所有處理。

例如:

RewriteRule ^api/v1/ api_v1.php [NC,END]

(我刪除了(.*)$正則表達式的尾隨,因為這裡沒有必要,並且使正則表達式的效率略微降低。)

但是,對現有.php文件的請求被您以後的規則重寫的事實確實表明您的第二條和第三條規則似乎沒有按預期工作……

#2. Rewrite to index.php except for for design/document/favicon/robots files that exists
RewriteCond %{REQUEST_URI} !.(css|js|png|jpg|jpeg|bmp|gif|ttf|eot|svg|woff|woff2|ico|webp|pdf)$
RewriteCond %{REQUEST_URI} !^(robots\.txt|favicon\.ico)$ [OR]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L]

#3. Rewrite enything else
RewriteRule ^(.*)$ index.php [L]

如果您請求something.php,則第一個條件成功(因為.php包含在列表中)。第二個條件也是成功的(不是or )。由於第二個條件與第三個條件進行了顯式或運算,因此不處理第三個條件。所有條件都成功,請求被重寫(不管是否存在)。robots.txt``favicon.ico``index.php``something.php

另一方面,如果您請求,resource.css則第一個條件失敗(因為它具有.css副檔名)。由於此條件是隱式 AND,因此不會處理其他條件並且不會觸發規則。然後第三條規則無條件地重寫resource.cssindex.php,無論它是否存在!

因此,這不會檢查所請求的資源(cssjpg等)是否“存在”,正如前面的評論所暗示的那樣。第三個條件(檢查請求未映射到文件)僅在前面的 OR 條件失敗且第一個條件成功時才會處理。換句話說,僅在您請求時才處理第三個條件/robots.txt- 我敢肯定這不是您的意圖。

換句話說,規則 #2 和 #3(儘管它們看起來很複雜)似乎將幾乎所有內容都重寫為index.php.

完整的解決方案

根據您的補充意見:

  • 對於文件類型的子集,提供資源 - 如果它存在。如果資源不存在,則將請求發送到index.php.
  • 對於其他文件類型,index.php無論該資源是否存在,都將請求發送到。
  • 一些請求(即/robots.txt/favicon.ico)不應發送到index.php.

請嘗試以下操作:

# Prevent 301 redirect with slash when folder exists and does not have slash appended
# This is not a security issue here since a PHP router is used and all the paths are redirected
DirectorySlash Off

# Since "DirectorySlash Off" is set, ensure that mod_auotindex directory listings are disabled
Options -Indexes

RewriteEngine On

#1. Rewrite for API url
RewriteRule ^api/v1/ api_v1.php [NC,END]

#2. Known URLs/files are served directly
RewriteRule ^(index\.php|robots\.txt|favicon\.ico)$ - [END]

#3. Certain file types (resources) are served directly if they exist
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule \.(css|js|png|jpe?g|bmp|gif|ttf|eot|svg|woff|woff2|ico|webp|pdf)$ - [END]

#4. Rewrite everything else
RewriteRule ^ index.php [END]

對於需要直接提供服務的任何內容,規則都會提前結束,因此我們只需要index.php在最後一條規則中重寫一次。

請注意,我已包含index.php在規則 #2 中。這是一種優化,但如果在最後一條規則上使用END標誌(而不是),則不是絕對必要的。L

或者,您可以選擇將任何直接請求重定向index.php回根目錄(為搜尋引擎規範化 URL)。這是以防萬一/index.php被暴露(或猜測)給最終使用者。

例如,在 API 的第一條規則之後添加以下內容:

#1.5 Redirect direct requests to "/index.php" back to "/" (root)
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^index\.php$ / [R=301,L]

檢查環境變數的條件REDIRECT_STATUS對於防止重定向循環是必要的 - 以避免重定向重寫的 URL。END(雖然,如果在最後/重寫規則上使用標誌,這不是嚴格要求的。)

引用自:https://serverfault.com/questions/1094447