簡體   English   中英

參考:mod_rewrite、URL 重寫和“漂亮鏈接”解釋

[英]Reference: mod_rewrite, URL rewriting and "pretty links" explained

“漂亮的鏈接”是一個經常被要求的話題,但很少被完全解釋。 mod_rewrite是制作“漂亮鏈接”的一種方法,但它很復雜,而且它的語法非常簡潔,難以理解,並且文檔假定對 HTTP 有一定的熟練程度。 有人可以簡單地解釋“漂亮鏈接”如何工作以及如何使用 mod_rewrite 來創建它們嗎?

其他通用名稱、別名、干凈 URL 的術語: RESTful URL、用戶友好 URL、 SEO友好 URL、 slugging和 MVC URL(可能用詞不當)

要了解 mod_rewrite 是什么,您首先需要了解 Web 服務器的工作原理。 Web 服務器響應HTTP 請求 最基本的 HTTP 請求如下所示:

GET /foo/bar.html HTTP/1.1

這是瀏覽器向 Web 服務器請求URL /foo/bar.html的簡單請求。 重要的是要強調它不請求文件,它只請求一些任意 URL。 請求也可能如下所示:

GET /foo/bar?baz=42 HTTP/1.1

這與對 URL 的請求一樣有效,而且更明顯地與文件無關。

Web 服務器是一個偵聽端口的應用程序,接受來自該端口的 HTTP 請求並返回響應。 Web 服務器可以完全自由地以它認為合適的任何方式/以您配置的任何方式響應任何請求。 這個響應不是一個文件,它是一個HTTP 響應,它可能與任何磁盤上的物理文件有任何關系,也可能沒有任何關系。 Web 服務器不一定是 Apache,還有許多其他的 Web 服務器,它們都是持續運行的程序,並連接到響應 HTTP 請求的端口。 你可以自己寫一個。 本段旨在讓您擺脫 URL 直接等於文件的任何概念,理解這一點非常重要。 :)

大多數 Web 服務器的默認配置是在硬盤上查找與 URL 匹配的文件。 如果服務器的文檔根目錄設置為/var/www ,它可能會查看文件/var/www/foo/bar.html是否存在,如果存在則提供它。 如果文件以“.php”結尾,它將調用 PHP 解釋器,然后返回結果。 所有這些關聯都是完全可配置的; 一個文件不必以“.php”結尾,Web 服務器就可以通過 PHP 解釋器運行它,並且 URL 不必匹配磁盤上的任何特定文件以發生某些事情。

mod_rewrite 是一種重寫內部請求處理的方法。 當 Web 服務器收到對 URL /foo/bar的請求時,您可以將該 URL重寫為其他內容,然后 Web 服務器將在磁盤上查找與之匹配的文件。 簡單的例子:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

這條規則說,只要請求匹配“/foo/bar”,就將其重寫為“/foo/baz”。 然后將處理該請求,就好像/foo/baz已被請求一樣。 這可用於各種效果,例如:

RewriteRule (.*) $1.html

此規則匹配任何內容( .* )並捕獲它( (..) ),然后將其重寫為附加“.html”。 換句話說,如果/foo/bar是請求的 URL,它將被處理為好像/foo/bar.html已被請求。 有關正則表達式匹配、捕獲和替換的更多信息,請參閱http://regular-expressions.info

另一個經常遇到的規則是:

RewriteRule (.*) index.php?url=$1

這再次匹配任何內容並將其重寫為文件 index.php,並將最初請求的 URL 附加在url查詢參數中。 即,對於傳入的任何和所有請求,都會執行文件 index.php 並且該文件將可以訪問$_GET['url']中的原始請求,因此它可以用它做任何事情。

首先,您將這些重寫規則放入您的Web 服務器配置文件中。 Apache 還允許*您將它們放入文檔根目錄中名為.htaccess的文件中(即在 .php 文件旁邊)。

*如果主 Apache 配置文件允許; 它是可選的,但經常啟用。

mod_rewrite不做什么

mod_rewrite 不會神奇地使您的所有 URL 都“漂亮”。 這是一個常見的誤解。 如果您的網站中有此鏈接:

<a href="/my/ugly/link.php?is=not&amp;very=pretty">

沒有什么 mod_rewrite 可以做的漂亮。 為了使它成為一個漂亮的鏈接,您必須:

  1. 將鏈接更改為漂亮的鏈接:

     <a href="/my/pretty/link">
  2. 在服務器上使用 mod_rewrite 以使用上述任何一種方法處理對 URL /my/pretty/link的請求。

(可以結合使用mod_substitute來轉換傳出的 HTML 頁面及其包含的鏈接。盡管這通常比更新 HTML 資源更費力。)

mod_rewrite 可以做很多事情,您可以創建非常復雜的匹配規則,包括鏈接多個重寫、將請求代理到完全不同的服務或機器、返回特定的 HTTP 狀態代碼作為響應、重定向請求等。它非常強大,可用於如果您了解基本的 HTTP 請求-響應機制,那就太好了。 不會自動使您的鏈接漂亮。

有關所有可能的標志和選項,請參閱官方文檔

為了擴展deceze 的答案,我想提供一些示例並解釋其他一些 mod_rewrite 功能。

以下所有示例都假定您已經在.htaccess文件中包含了RewriteEngine On

重寫示例

讓我們舉這個例子:

RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]

該規則分為 4 個部分:

  1. RewriteRule - 啟動重寫規則
  2. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - 這稱為模式,但我將其稱為左側規則的 - 你想要重寫的內容
  3. blog/index.php?id=$1&title=$2 - 稱為替換,或重寫規則的右側 - 您要重寫的內容
  4. [NC,L,QSA]是重寫規則的標志,用逗號分隔,我稍后會詳細解釋

上面的重寫將允許您鏈接到/blog/1/foo/之類的東西,它實際上會加載/blog/index.php?id=1&title=foo

規則的左側

  • ^表示頁面名稱的開頭 - 因此它將重寫example.com/blog/...但不會重寫 example.com/foo/blog example.com/foo/blog/...
  • 每組(…)括號代表一個正則表達式,我們可以將其捕獲為規則右側的變量。 在這個例子中:
    • 第一組括號 - ([0-9]+) - 匹配長度至少為 1 個字符且僅具有數值(即 0-9)的字符串。 這可以用規則右側的$1來引用
    • 第二組括號匹配長度至少為 1 個字符的字符串,僅包含字母數字字符(AZ、az 或 0-9)或-+ (注意+用反斜杠轉義,因為沒有轉義它會執行作為正則表達式重復字符)。 這可以用規則右側的$2來引用
  • ? 表示前面的字符是可選的,所以在這種情況下/blog/1/foo//blog/1/foo都會重寫到同一個地方
  • $表示這是我們要匹配的字符串的結尾

標志

這些選項添加在重寫規則末尾的方括號中以指定某些條件。 同樣,您可以在文檔中閱讀許多不同的標志,但我將介紹一些更常見的標志:

NC

no case 標志意味着重寫規則不區分大小寫,因此對於上面的示例規則,這意味着/blog/1/foo//BLOG/1/foo/ (或任何變體)都將匹配。

L

最后一個標志表明這是應處理的最后一條規則。 這意味着當且僅當此規則匹配時,不會在當前重寫處理運行中評估進一步的規則。 如果規則不匹配,將照常嘗試所有其他規則。 如果您不設置L標志,則以下所有規則將應用於之后重寫的 URL。

END

從 Apache 2.4 開始,您還可以使用[END]標志。 與之匹配的規則將完全終止進一步的別名/重寫處理。 (而[L]標志通常會觸發第二輪,例如在重寫子目錄或從子目錄中重寫時。)

QSA

查詢字符串附加標志允許我們將額外的變量傳遞給指定的 URL,這些變量將被添加到原始 get 參數中。 對於我們的示例,這意味着/blog/1/foo/?comments=15之類的內容將加載/blog/index.php?id=1&title=foo&comments=15

R

這個標志不是我在上面的示例中使用的標志,但我認為值得一提。 這允許您指定 http 重定向,並可選擇包含狀態代碼(例如R=301 )。 例如,如果您想在 /myblog/ 上執行 301 重定向到 /blog/,您只需編寫如下規則:

RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]

重寫條件

重寫條件使重寫更加強大,允許您為更具體的情況指定重寫。 您可以在文檔中閱讀很多條件,但我將介紹一些常見示例並進行解釋:

# if the host doesn't start with www. then add it and redirect
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

這是一種非常常見的做法,它會在您的域前面加上www. (如果它不存在)並執行 301 重定向。 例如,加載http://example.com/blog/它會將您重定向到http://www.example.com/blog/

# if it cant find the image, try find the image on another domain
RewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]

這稍微不太常見,但是如果文件名是服務器上存在的目錄或文件,則不執行規則的一個很好的示例。

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]只會對文件擴展名為 jpg、jpeg、gif 或 png(不區分大小寫)的文件執行重寫。
  • %{REQUEST_FILENAME} !-f將檢查文件是否存在於當前服務器上,如果不存在則只執行重寫
  • %{REQUEST_FILENAME} !-d將檢查文件是否存在於當前服務器上,如果不存在則僅執行重寫
  • 重寫將嘗試在另一個域上加載相同的文件

參考

Stack Overflow 還有許多其他很棒的入門資源:

甚至對新手友好的正則表達式概述:

常用占位符

  • .*匹配任何內容,甚至是空字符串。 您不想在任何地方都使用此模式,但通常在最后一個后備規則中使用。
  • [^/]+更常用於路徑段。 它匹配除正斜杠之外的任何內容。
  • \d+只匹配數字字符串。
  • \w+匹配字母數字字符。 它基本上是[A-Za-z0-9_]的簡寫。
  • [\w\-]+用於“slug”風格的路徑段,使用字母、數字、破折號-_
  • [\w\-.,]+添加句點和逗號。 更喜歡在[…]字符類中使用轉義的\-破折號。
  • \. 表示字面時間。 否則. […]之外是任何符號的占位符。

這些占位符中的每一個通常都包含在(…)括號中作為捕獲組。 而整個模式通常在^………$開始 + 結束標記。 引用“模式”是可選的。

重寫規則

以下示例以 PHP 為中心,並且更加增量,更容易適應類似情況。 它們只是摘要,通常鏈接到更多變體或詳細問答。

  • 靜態映射
    /contact/about

    將一些頁面名稱縮短為內部文件方案是最簡單的:

     RewriteRule ^contact$ templ/contact.html RewriteRule ^about$ about.php
  • 數字標識符
    /object/123

    http://example.com/article/531類的快捷方式引入現有 PHP 腳本也很容易。 數字占位符可以重新映射到$_GET參數:

     RewriteRule ^article/(\d+)$ article-show.php?id=$1 # └───────────────────────────┘
  • 蛞蝓式占位符
    /article/with-some-title-slug

    您可以輕松地擴展該規則以允許/article/title-string占位符:

     RewriteRule ^article/([\w-]+)$ article-show.php?title=$1 # └────────────────────────────────┘

    請注意,您的腳本必須能夠(或適應)將這些標題映射回數據庫 ID。 單獨的 RewriteRules 不能憑空創建或猜測信息。

  • 帶有數字前綴的蛞蝓
    /readable/123-plus-title

    因此,您會經常看到在實踐中使用的混合/article/529-title-slug路徑:

     RewriteRule ^article/(\d+)-([\w-]+)$ article.php?id=$1&title=$2 # └───────────────────────────────┘

    現在您無論如何都可以跳過傳遞title=$2 ,因為您的腳本通常會依賴於 database-id 。 -title-slug已成為任意 URL 裝飾。

  • 與替代列表的一致性
    /foo/… /bar/… /baz/…

    如果您對多個虛擬頁面路徑有類似的規則,那么您可以使用|匹配和壓縮它們。 替代清單。 再次將它們重新分配給內部 GET 參數:

     # ┌─────────────────────────┐ RewriteRule ^(blog|post|user)/(\w+)$ disp.php?type=$1&id=$2 # └───────────────────────────────────┘

    如果這變得太復雜,您可以將它們拆分為單獨的RewriteRule

  • 將相關 URL 分派到不同的后端
    /date/SWITCH/backend

    替代列表的更實際用途是將請求路徑映射到不同的腳本。 例如,根據日期為較舊和較新的 Web 應用程序提供統一的 URL:

     # ┌─────────────────────────────┐ # │ ┌───────────┼───────────────┐ RewriteRule ^blog/(2009|2010|2011)/([\d-]+)/?$ old/blog.php?date=$2 RewriteRule ^blog/(\d+)/([\d-]+)/?$ modern/blog/index.php?start=$2 # └──────────────────────────────────────┘

    這只是將 2009-2011 年的帖子重新映射到一個腳本,並將所有其他年份隱式地重新映射到另一個處理程序。 請注意更具體的規則首先出現 每個腳本可能使用不同的 GET 參數。

  • 除了/路徑斜杠之外的其他分隔符
    /user-123-name

    您最常看到 RewriteRules 來模擬虛擬目錄結構。 但你不會被迫缺乏創造力。 您也可以使用-連字符進行分段或結構。

     RewriteRule ^user-(\d+)$ show.php?what=user&id=$1 # └──────────────────────────────┘ # This could use `(\w+)` alternatively for user names instead of ids.

    對於同樣常見的/wiki:section:Page_Name方案:

     RewriteRule ^wiki:(\w+):(\w+)$ wiki.php?sect=$1&page=$2 # └─────┼────────────────────┘ │ # └────────────────────────────┘

    有時它適合在/ -delimiters 和:或 之間交替使用. 甚至在同一規則中。 或者再次使用兩個 RewriteRules 將變體映射到不同的腳本。

  • 可選的尾隨/斜杠
    /dir = /dir/

    選擇目錄式的路徑時,您可以在有沒有最終的情況下達到它

     RewriteRule ^blog/([\w-]+)/?$ blog/show.php?id=$1 # ┗┛

    現在這處理http://example.com/blog/123/blog/123/ 並且/?$方法很容易附加到任何其他 RewriteRule 上。

  • 虛擬路徑的靈活段
    .*/.*/.*/.*

    您將遇到的大多數規則將一組受限的/…/資源路徑段映射到單個 GET 參數。 然而,一些腳本處理可變數量的選項 Apache regexp 引擎不允許選擇任意數量的它們。 但是您可以自己輕松地將其擴展為規則塊:

     Rewriterule ^(\w+)/?$ in.php?a=$1 Rewriterule ^(\w+)/(\w+)/?$ in.php?a=$1&b=$2 Rewriterule ^(\w+)/(\w+)/(\w+)/?$ in.php?a=$1&b=$2&c=$3 # └─────┴─────┴───────────────────┴────┴────┘

    如果您需要最多五個路徑段,則將此方案復制到五個規則中。 您當然可以使用更具體的[^/]+占位符。 在這里,排序並不重要,因為兩者都不重疊。 因此,首先擁有最常用的路徑是可以的。

    或者,您可以在此處通過?p[]=$1&p[]=$2&p[]=3查詢字符串使用 PHP 數組參數 - 如果您的腳本只是更喜歡預先拆分它們。 (雖然更常見的是只使用一個包羅萬象的規則,並讓腳本本身將這些段從 REQUEST_URI 中擴展出來。)

    另請參閱:如何將我的 URL 路徑段轉換為查詢字符串鍵值對?

  • 可選段
    prefix/opt?/.*

    一個常見的變體是規則中具有可選前綴。 如果您有靜態字符串或更受限制的占位符,這通常是有意義的:

     RewriteRule ^(\w+)(?:/([^/]+))?/(\w+)$ ?main=$1&opt=$2&suffix=$3

    現在更復雜的模式(?:/([^/])+)? 那里簡單地包裝了一個非捕獲(?:…)組,並使其成為可選的)? . 包含的占位符([^/]+)將是替換模式$2 ,但如果沒有中間/…/路徑,則為空。

  • 捕獲剩余部分
    /prefix/123-capture/…/*/…whatever…

    如前所述,您通常不需要過於通用的重寫模式。 但是,有時將靜態和特定比較與.*結合起來確實有意義。

     RewriteRule ^(specific)/prefix/(\d+)(/.*)?$ speci.php?id=$2&otherparams=$2

    這可選擇任何/…/…/…尾隨路徑段。 然后當然需要處理腳本將它們拆分,並對提取的參數本身進行可變化(這就是Web- “MVC”框架所做的)。

  • 尾隨文件“擴展名”
    /old/path.HTML

    URL 並沒有真正的文件擴展名。 這就是整個參考的內容(= URL 是虛擬定位器,不一定是直接文件系統映像)。 但是,如果您之前有 1:1 文件映射,則可以制定更簡單的規則:

     RewriteRule ^styles/([\w\.\-]+)\.css$ sass-cache.php?old_fn_base=$1 RewriteRule ^images/([\w\.\-]+)\.gif$ png-converter.php?load_from=$2

    其他常見用途是將過時的.html路徑重新映射到更新的.php處理程序,或者僅為單個(實際/真實)文件的目錄名稱別名。

  • 乒乓球(統一重定向和重寫)
    /ugly.html ←→ /pretty

    因此,在某些時候,您正在重寫 HTML 頁面以僅攜帶漂亮的鏈接,如deceze 所述 同時,您仍然會收到對路徑的請求,有時甚至是來自書簽的請求。 作為解決方法,您可以通過 ping-pong 瀏覽器顯示/建立新的 URL。

    這個常見的技巧涉及每當傳入的 URL 遵循過時/丑陋的命名方案時發送 30x/Location重定向 然后瀏覽器將重新請求新的/漂亮的 URL,然后將其重寫(僅在內部)到原始或新位置。

     # redirect browser for old/ugly incoming paths RewriteRule ^old/teams\.html$ /teams [R=301,QSA,END] # internally remap already-pretty incoming request RewriteRule ^teams$ teams.php [QSA,END]

    請注意此示例如何僅使用[END]而不是[L]來安全地交替。 對於較舊的 Apache 2.2 版本,您可以使用其他解決方法,除了重新映射查詢字符串參數之外,例如: 重定向丑陋到漂亮的 URL,重新映射回丑陋的路徑,沒有無限循環

  • 模式中的空格
    /this+that+

    它在瀏覽器地址欄中不是那么漂亮,但您可以在 URL 中使用空格。 對於重寫模式,使用反斜杠轉義的\␣空格。 否則只需" - 引用整個模式或替換:

     RewriteRule "^this [\w ]+/(.*)$" "index.php?id=$1" [L]

    客戶端使用+%20對空格進行序列化。 然而在 RewriteRules 中,它們被解釋為所有相對路徑段的文字字符。

頻繁重復:

  • 包羅萬象的中央調度程序/前端控制器腳本

     RewriteCond %{REQUEST_URI} !-f RewriteCond %{REQUEST_URI} !-d RewriteRule ^.*$ index.php [L]

    PHP 框架或 WebCMS / 門戶腳本經常使用它。 然后在 PHP 中使用$_SERVER["REQUEST_URI"]處理實際的路徑拆分。 所以從概念上講,它與“per mod_rewrite”的 URL 處理幾乎相反。 (只需使用FallBackResource代替。)

  • 刪除www. 從主機名

    請注意,這不會復制查詢字符串等。

     # ┌──────────┐ RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] │ RewriteRule ^(.*)$ http://%1/$1 [R=301,L] │ # ↓ └───┼────────────┘ # └───────────────┘

    也可以看看:
    · .htaccess 中不同協議的 URL 重寫
    · 通用 htaccess 將 www 重定向到非 www
    · .htaccess - 如何強制“www”。 以一般的方式?

    請注意,RewriteCond/RewriteRule 組合可能更復雜,匹配項( %1$1 )在兩個方向上交互,甚至:

    RewriteRule 和 RewriteCond 之間的引用 %1 和 $2、%3
    Apache 手冊 - mod_rewrite 介紹,版權所有 2015 The Apache Software Foundation, AL-2.0

  • 重定向到HTTPS://

     RewriteCond %{SERVER_PORT} 80 RewriteRule ^(.*)$ https://example.com/$1 [R,L]

    另見: https ://wiki.apache.org/httpd/RewriteHTTPToHTTPS

  • “刪除” PHP 擴展

     RewriteCond %{REQUEST_FILENAME}.php -f RewriteRule ^(.+)$ $1.php [L] # or [END]

    另請參閱: 使用 mod_rewrite 刪除 .php 擴展名

  • 將舊的 .html 路徑別名為 .php 腳本

    請參閱: http ://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility

  • 從“/page”之類的 URL 重寫為“/index.php/page”之類的腳本

    請參閱mod_rewrite、php 和 .htaccess 文件

  • 將子域重定向到文件夾

    請參閱如何讓我的 htaccess 正常工作(子域)?

普遍存在.htaccess陷阱

現在把這個和一粒鹽一起吃。 並非每個建議都可以推廣到所有情況。 這只是對眾所周知的和一些不明顯的絆腳石的簡單總結:

  • 啟用mod_rewrite.htaccess

    要在每個目錄的配置文件中實際使用 RewriteRules,您必須:

    • 檢查您的服務器是否啟用了AllowOverride All 否則,您的每個目錄的.htaccess指令將被忽略,並且 RewriteRules 將不起作用。

    • 顯然在您的httpd.conf模塊部分中啟用了mod_rewrite

    • 仍然在每個規則列表前面加上RewriteEngine On 雖然 mod_rewrite 在<VirtualHost><Directory>部分中隱式處於活動狀態,但每個目錄的.htaccess文件需要單獨調用它。

  • 前導斜杠^/不匹配

    您通常不應該使用^/開始您的.htaccess RewriteRule 模式:

     RewriteRule ^/article/\d+$ … ↑

    這在舊教程中很常見。 它曾經對古老的 Apache 1.x 版本是正確的。 如今,請求路徑在.htaccess RewriteRules 中很方便地完全與目錄相關 只留下領先/出局。

    · 請注意,雖然<VirtualHost>部分中的前導斜杠仍然是正確的。 這就是為什么你經常看到它^/? 可選的規則奇偶校驗。
    · 或者當使用RewriteCond %{REQUEST_URI}時,您仍然會匹配前導/
    · 另請參閱Webmaster.SE:mod_rewrite 模式中何時需要前導斜杠 (/)?

  • <IfModule *>包裝器消失了!

    您可能已經在許多示例中看​​到了這一點:

     <IfModule mod_rewrite.c> Rewrite… </IfModule>
    • <VirtualHost>部分中確實有意義 - 如果它與另一個后備選項結合使用,例如 ScriptAliasMatch。 (但從來沒有人這樣做過)。
    • 它通常用於許多開源項目的默認.htaccess規則集。 在那里它只是作為后備,並保持“丑陋”的 URL 作為默認工作。

    但是,您通常不希望在您自己的.htaccess文件中使用它。

    • 首先, mod_rewrite 不會隨機脫離。 (如果是這樣,你會有更大的問題)。
    • 如果它真的被禁用,你的 RewriteRules 仍然無法正常工作。
    • 它旨在防止 HTTP 500錯誤。 它通常完成的是用 HTTP 404錯誤代替您的用戶。 (如果您考慮一下,它不會對用戶更加友好。)
    • 實際上,它只是抑制了更有用的日志條目或服務器通知郵件。 不會知道為什么你的 RewriteRules 永遠不會工作。

    看似誘人的普遍保障措施,往往成為實踐中的障礙。

  • 除非需要,否則不要使用RewriteBase

    許多復制+粘貼示例包含RewriteBase /指令。 無論如何,這恰好是隱含的默認值。 所以你實際上並不需要這個。 這是花哨的 VirtualHost 重寫方案的解決方法,並且錯誤地猜測了某些共享主機的 DOCUMENT_ROOT 路徑。

    在更深的子目錄中與單個 Web 應用程序一起使用是有意義的。 在這種情況下,它可以縮短 RewriteRule 模式。 通常,最好在每個目錄規則集中使用相對路徑說明符。

    另請參閱RewriteBase 如何在 .htaccess 中工作

  • 虛擬路徑重疊時禁用MultiViews

    URL 重寫主要用於支持虛擬傳入路徑。 通常,您只有一個調度程序腳本( index.php )或幾個單獨的處理程序( articles.phpblog.phpwiki.php ,...)。 后者可能與類似的虛擬 RewriteRule 路徑發生沖突

    例如,對/article/123的請求可以使用/123 PATH_INFO 隱式映射到article.php 您要么必須使用普通的RewriteCond !-f + !-d來保護您的規則,和/或禁用 PATH_INFO 支持,或者只是禁用Options -MultiViews

    這並不是說您總是必須這樣做 內容協商只是虛擬資源的一種自動化。

  • 下單很重要

    如果您還沒有,請查看您想知道的關於 mod_rewrite 的所有信息 組合多個 RewriteRules 通常會導致交互。 這不是每個[L]標志習慣性地阻止的事情,而是您一旦精通就會接受的方案。 可以重新重新編寫從一條規則到另一條規則的虛擬路徑,直到它到達實際的目標處理程序。

    盡管如此,您仍然經常希望在早期規則中擁有最具體的規則(固定字符串/forum/…模式,或更嚴格的占位符[^/.]+ )。 通用的 slurp-all 規則 ( .* ) 最好留給后面的規則。 (一個例外是RewriteCond -f/-d保護作為主要塊。)

  • 樣式表和圖像停止工作

    當您引入虛擬目錄結構/blog/article/123時,這會影響 HTML 中的相對資源引用(例如<img src=mouse.png> )。 可以通過以下方式解決:

    • 僅使用服務器絕對引用href="/old.html"src="/logo.png"
    • 通常只需將<base href="/index">添加到您的 HTML <head>部分。 這隱含地將相對引用重新綁定到它們之前的內容。

    您也可以制作進一步的 RewriteRules 以將.css.png路徑重新綁定到其原始位置。 但這都是不必要的,或者會導致額外的重定向並妨礙緩存。

    另請參閱: CSS、JS 和圖像不顯示漂亮的 url

  • RewriteConds 只屏蔽一個 RewriteRule

    一個常見的誤解是 RewriteCond 會阻止多個 RewriteRules(因為它們在視覺上排列在一起):

     RewriteCond %{SERVER_NAME} localhost RewriteRule ^secret admin/tools.php RewriteRule ^hidden sqladmin.cgi

    默認情況下它不會。 您可以使用[S=2]標志鏈接它們 否則,您將不得不重復它們。 雖然有時您可以制定一個“倒置”的主要規則來提早 [END] 重寫處理。

  • QUERY_STRING 免於 RewriteRules

    您無法匹配RewriteRule index.php\?x=y ,因為 mod_rewrite 僅與默認情況下的相對路徑進行比較。 您可以通過以下方式單獨匹配它們:

     RewriteCond %{QUERY_STRING} \b(?:param)=([^&]+)(?:&|$) RewriteRule ^add/(.+)$ add/%1/$1 # ←──﹪₁──┘

    另請參閱如何將查詢字符串變量與 mod_rewrite 匹配?

  • .htaccess<VirtualHost>

    如果您在每個目錄的配置文件中使用 RewriteRules,那么擔心正則表達式的性能是沒有意義的。 Apache 保留編譯后的 PCRE 模式的時間比使用通用路由框架的 PHP 進程長。 但是,對於高流量站點,一旦經過實戰測試,您應該考慮將規則集移動到虛擬主機服務器配置中。

    在這種情況下,更喜歡可選的^/? 目錄分隔符前綴。 這允許在 PerDir 和服務器配置文件之間自由移動 RewriteRules。

  • 每當某些事情不起作用

    不要擔心。

    • 比較access.logerror.log

      通常你可以通過查看你的error.logaccess.log來弄清楚 RewriteRule 的行為異常。 關聯訪問時間以查看最初進入的請求路徑,以及 Apache 無法解析到的路徑/文件(錯誤 404/500)。

      這並沒有告訴您哪個 RewriteRule 是罪魁禍首。 但是像/docroot/21-.itle?index.php這樣不可訪問的最終路徑可能會泄露進一步檢查的位置。 否則禁用規則,直到你得到一些可預測的路徑。

    • 啟用重寫日志

      請參閱Apache RewriteLog文檔。 對於調試,您可以在虛擬主機部分啟用它:

       # Apache 2.2 RewriteLogLevel 5 RewriteLog /tmp/rewrite.log # Apache 2.4 LogLevel alert rewrite:trace5 #ErrorLog /tmp/rewrite.log

      這產生了每個規則如何修改傳入請求路徑的詳細摘要:

       [..] applying pattern '^test_.*$' to uri 'index.php' [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php [..] applying pattern '^index\.php$' to uri 'index.php'

      這有助於縮小過於通用的規則和正則表達式的失誤。

      也可以看看:
      · .htaccess 不工作 (mod_rewrite)
      · 調試.htaccess 重寫規則的技巧

    • 在問自己的問題之前

      您可能知道,Stack Overflow 非常適合在 mod_rewrite 上提問。 通過包括先前的研究和嘗試(避免多余的答案)使它們成為主題,展示基本的理解,並且:

      • 包括輸入 URL 的完整示例、錯誤重寫的目標路徑、您的真實目錄結構。
      • 完整的 RewriteRule 集,但挑出假定的有缺陷的集。
      • Apache 和 PHP 版本、操作系統類型、文件系統、DOCUMENT_ROOT 和 PHPs $_SERVER環境(如果它與參數不匹配有關)。
      • 您的access.logerror.log的摘錄,用於驗證現有規則解析的內容。 更好的是,一個rewrite.log總結。

      這可以更快,更准確地獲得答案,並使它們對其他人更有用。

  • 評論您的.htaccess

    如果您從某處復制示例,請注意包含# comment and origin link 雖然忽略歸因只是一種不禮貌的做法,但它通常確實會損害以后的維護。 記錄任何代碼或教程源。 特別是在不熟悉的情況下,您應該更感興趣的是不要將它們視為魔法黑匣子。

  • 這不是“SEO”-URL

    免責聲明:只是一個小問題。 您經常聽到漂亮的 URL 重寫方案被稱為“SEO”鏈接或其他東西。 雖然這對於搜索示例很有用,但它是一個過時的誤稱。

    現代搜索引擎都不會真正受到路徑段中的.html.php?id=123查詢字符串的干擾。 舊的搜索引擎,例如 AltaVista,確實避免了抓取具有潛在不明確訪問路徑的網站。 現代爬蟲通常甚至渴望深度網絡資源。

    從概念上講,“漂亮”的 URL 應該用於使網站對用戶友好

    1. 具有可讀且明顯的資源方案。
    2. 確保 URL 是長期存在的(AKA永久鏈接)。
    3. 通過/common/tree/nesting提供可發現性。

    但是,不要為了順從而犧牲獨特的要求。

工具

有各種在線工具可以為大多數 GET 參數 URL 生成 RewriteRules:

大多數情況下只輸出[^/]+通用占位符,但對於瑣碎的站點可能就足夠了。

mod_rewrite 的替代品

許多基本的虛擬 URL 方案可以在不使用 RewriteRules 的情況下實現。 Apache 允許在沒有.php擴展名的情況下調用 PHP 腳本,並使用虛擬PATH_INFO參數。

  1. 使用PATH_INFO ,盧克

    現在AcceptPathInfo On通常默認啟用。 這基本上允許.php和其他資源 URL 攜帶一個虛擬參數:

     http://example.com/script.php/virtual/path

    現在這個/virtual/path在 PHP 中顯示為$_SERVER["PATH_INFO"] ,您可以在其中處理任何您喜歡的額外參數。

    這不像讓 Apache 將輸入路徑段分成$1$2$3並將它們作為不同的$_GET變量傳遞給 PHP 那樣方便。 它只是用更少的配置工作來模擬“漂亮的 URL”。

  2. 啟用MultiViews以隱藏.php擴展名

    在 URL 中避免使用.php “文件擴展名”的最簡單選項是啟用:

     Options +MultiViews

    由於匹配的基本名稱,這使 Apache 為/article上的 HTTP 請求選擇article.php 這與前面提到的 PATH_INFO 功能配合得很好。 因此,您可以只使用http://example.com/article/virtual/title之類的 URL。 如果您有一個具有多個 PHP 調用點/腳本的傳統 Web 應用程序,這很有意義。

    請注意,MultiViews 具有不同/更廣泛的用途。 它會導致非常小的性能損失,因為 Apache 總是尋找具有匹配基本名稱的其他文件。 它實際上是用於Content-Negotiation ,因此瀏覽器會在可用資源(例如article.en.phparticle.fr.phparticle.jp.mp4 )中獲得最佳選擇。

  3. 用於無擴展名.php腳本的 SetType 或 SetHandler

    避免在 URL 中攜帶.php后綴的更直接的方法是為其他文件方案配置 PHP 處理程序 最簡單的選項是通過.htaccess覆蓋默認的 MIME/處理程序類型:

     DefaultType application/x-httpd-php

    這樣,您可以將您的article.php腳本重命名為僅article (不帶擴展名),但仍將其作為 PHP 腳本處理。

    現在這可能會對安全性和性能產生一些影響,因為現在所有無擴展名文件都將通過 PHP 進行管道傳輸。 因此,您也可以只為單個文件設置此行為:

     <Files article> SetHandler application/x-httpd-php # or SetType </Files>

    這在某種程度上取決於您的服務器設置和使用的 PHP SAPI。 常見的替代方案包括ForceType application/x-httpd-phpAddHandler php5-script

    再次注意,此類設置會從一個.htaccess傳播到子文件夾。 您始終應該禁用靜態資源和上傳/目錄等的腳本執行( SetHandler NoneOptions -Execphp_flag engine off等)。

  4. 其他 Apache 重寫方案

    在其眾多選項中,Apache 提供了mod_alias功能——有時它的工作原理與mod_rewrite的 RewriteRules 一樣好。 請注意,其中大部分必須在<VirtualHost>部分中設置,而不是在每個目錄的.htaccess配置文件中。

    • ScriptAliasMatch主要用於 CGI 腳本,但也應該適用於 PHP。 它允許正則表達式,就像任何RewriteRule一樣。 事實上,它可能是配置一個包羅萬象的前端控制器的最強大的選擇。

    • 一個簡單的Alias也有助於一些簡單的重寫方案。

    • 即使是簡單的ErrorDocument指令也可以用來讓 PHP 腳本處理虛擬路徑。 請注意,這是一個笨拙的解決方法,但是禁止 GET 請求以外的任何內容,並根據定義淹沒 error.log。

    有關更多提示,請參閱http://httpd.apache.org/docs/2.2/urlmapping.html

一個關於 URL 重寫的常見問題是這樣的:

我目前有如下所示的 URL:

我把它們做成了這樣:

通過在我的 .htaccess 文件中使用它:

 RewriteRule my-blog/(\d+)--i-found-the-answer my-blog/entry.php?id=$1

但我希望它們看起來像這樣:

如何更改我的 .htaccess 文件以使其正常工作?


簡單的答案是你不能。

重寫規則不會使丑陋的 URL 變得漂亮,它們會使漂亮的 URL 變得丑陋

每當您在 Web 瀏覽器中輸入 URL、點擊鏈接或顯示引用圖像的頁面等時,瀏覽器都會請求特定 URL。 該請求最終到達 Web 服務器,Web 服務器給出響應

重寫規則只是一條規則,它說“當瀏覽器請求一個看起來像 X 的 URL 時,給他們與他們請求 Y 相同的響應”。

當我們制定規則來處理“漂亮的 URL”時,請求漂亮的 URL響應是基於內部丑陋的 URL 它不能反過來,因為我們正在服務器上編寫規則,而服務器看到的只是瀏覽器發送的請求。

你不能使用你沒有的信息

給定重寫規則的基本模型,假設您正在向人類發出指令。 你可以說:

  • 如果您在請求中看到一個數字,例如“http://example.com/my-blog/42--i-found-the-answer”中的“42”,請將該數字放在“my-博客/entry.php?id="

但是,如果請求中沒有信息,您的指示將沒有任何意義:

  • 如果請求中包含“my-blog”,例如“http://example.com/my-blog/i-found-the-answer”,請將正確的數字放在“my-blog/entry”的末尾。 php?id="

閱讀這些說明的人會說“對不起,我怎么知道正確的數字是多少?”

重定向:“此 URL 當前不在辦公室……”

有時,您會看到相反的規則,如下所示:

RewriteRule my-blog/entry.php?id=(\d+) my-blog/$1--i-found-the-answer [R]

此規則匹配左側的丑陋 URL,在右側生成漂亮的 URL。 所以我們肯定可以在漂亮部分的開頭不使用 ID 來編寫它嗎?

RewriteRule my-blog/entry.php?id=(\d+) my-blog/i-found-the-answer [R]

重要的區別是[R]標志,這意味着這個規則實際上是一個重定向——而不是“提供來自這個 URL 的響應”,它的意思是“告訴瀏覽器加載這個 URL”。

您可以將其想象為其中一封自動回復的電子郵件,內容為“抱歉,Joe Bloggs 目前正在度假;請將您的信息發送給 Jane Smith。” 同樣,上面的重定向告​​訴瀏覽器“對不起,沒有http://example.com/my-blog/entry.php?id=42的內容;請請求http://example.com/my-blog/42--i-found-the-answer代替。

這個類比的重點是,如果實際上沒有任何叫 Jane Smith 的人在那里工作,或者如果他們不知道如何回答 Joe Bloggs 通常處理的問題,那么上面的信息就沒有多大用處。 同樣,如果您告訴瀏覽器請求的 URL 實際上沒有做任何有用的事情,那么重定向也沒有用。 一旦瀏覽器跟隨重定向,它就會發出一個新的請求,當服務器收到新的請求時,它仍然不知道 ID 號是什么。

但是有些網站會這樣做,所以它一定是可能的!

Web 服務器只有請求中存在的信息,但它如何使用這些信息取決於您。

例如,您可以直接將其 URL 存儲在數據庫中,然后編寫一些代碼直接在 PHP、Python、node.js 等中進行匹配,而不是通過 ID 查找博客文章。或者您可以使用相同的 URL根據用戶在瀏覽器中設置的語言或基於 cookie 等顯示不同的內容。

您可以做的另一件事是使用帶有 POST 而不是 GET 方法的表單(或 API 請求)。 這意味着附加信息在請求的“正文”中發送,與 URL 分開。 它仍然必須發送,但在瀏覽器中不那么明顯,不會包含在書簽等中。

但是你不能在 .htaccess 文件中寫一行來創造奇跡。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM