URL 重寫新手指南

 

URL重寫是什麼?

大多數動態網站的URL中都含有變量,以告知站點哪些信息須要展現給用戶。好比像下面這個URL,會通知相關的腳本加載編號爲7的產品:php

XHTML正則表達式

 

1api

http://www.example.com/show_a_product.php?product_id=7瀏覽器

這種URL結構的問題在於它並不容易記憶。若是是在電話中也很難讀出來(使人驚訝的是有不少人經過這種方式傳遞URL)。搜索引擎和用戶都不能從URL中獲得有用的內容信息。你沒辦法從URL看出在這個頁面能買挪威的藍鸚鵡(的羽毛)。這種是至關標準的URL——也就是通常你在CMS網站上看到的那種。相比下面的URL:服務器

XHTMLdom

 

1wordpress

http://www.example.com/products/7/工具

這種URL更清晰,也更短,更容易記憶,也更容易被念出來。儘管如此,它仍是不能告訴別人它所指向的內容是什麼。可是咱們能夠更進一步:post

XHTML學習

 

1

http://www.example.com/parrots/norwegian-blue/

這就是咱們要的東西了。即便不看上下文,咱們也能夠從這個URL中看出你要找的東西就在這個頁面上。搜索引擎能夠將這個URL分割成單詞(搜索引擎會將URL中的連字符當作空格,可是下劃線不是),而後根據這些信息更好地判斷頁面內容。這種URL是易於記憶和傳遞的。

不幸的是,要讓服務器理解最後一種URL,須要咱們作一番工做。當URL發起一個請求,服務器須要知道如何處理URL,才能知道該返回給用戶什麼內容。URL重寫就是這種將最後一種URL「翻譯」成服務器能理解的語言的技術。

平臺和工具

根據你的服務器上運行的軟件,你可能已經有URL重寫模塊。若是沒有,大多主機都提供啓用或安裝相關模塊的功能,你能夠嘗試啓用它。

Apache啓用URL重寫是最簡單的,它一般帶有本身的內建URL重寫模塊——mod_rewrite,啓動和使用mod_rewrite就像上傳和命名文件同樣簡單。

IIS是微軟的服務器軟件,標配並不包含URL重寫的能力,可是有不少插件提供了這種功能。ISAPI_Rewrite 是我比較推薦一款插件,這是我發現的一款功能最接近mod_rewrite的插件。在這篇文章的最後附有ISAPI_Rewrite的安裝和配置說明。

下面的代碼是一些使用mod_rewrite的例子。

基本的URL重寫

首先咱們來看一個簡單的例子。咱們有一個網站,含有一個單獨的PHP腳本,展現了一個單獨的頁面,URL以下:

XHTML

 

1

http://www.example.com/pet_care_info_07_07_2008.php

咱們想要簡化URL,理想的URL像這樣:

XHTML

 

1

http://www.example.com/pet-care/

爲了讓這個URL有效,咱們須要讓服務器在內部將全部的「pet-care」的請求重定向到「pet_care_info_07_07_2008.php」。咱們但願這個工做在內部進行是由於咱們不但願用戶瀏覽器的地址欄發生改變。

爲了達到這個目的,咱們首先須要創建一個名爲「.htaccess」的文本文檔來存儲咱們的規則。這個文件必須命名成這樣(不能是「.htaccess.txt」或者「rules.htaccess」)。這個文件應該放在服務器的根目錄(本例中放在與 「pet_care_info_07_07_2008.php」 相同的目錄中)。可能那裏已經有一個.htaccess文件了,這時咱們就應該編輯這個文件而不是覆蓋它。

.htaccess文件是服務器的配置文件。若是文件中有錯誤,服務器會提示錯誤信息(一般錯誤代碼是500)。若是使用FTP協議向服務器發送文件,必須使用ASCII編碼傳輸,而不是BINARY。在本例中,這個文件有兩個做用:1. 通知Apache啓動重寫引擎; 2. 把咱們的重寫規則告訴Apache。所以咱們須要在這個文件中加入如下內容:

 

1

RewriteEngine On # Turn on the rewriting engine RewriteRule ^pet-care/?$ pet_care_info_01_02_2008.php [NC,L] # Handle requests for "pet-care"

有幾個須要注意的地方:在.htaccess文件中,‘#’以後的文字都會被當作註釋忽略,建議你們多使用註釋;「RewriteEngine」這一行在每一個.htaccess文件中應當只使用一次(請注意在後面的代碼中都不包括這一行)。

「RewriteRule」行是見證奇蹟的地方。這一行能夠分爲五個部分:

  • RewriteRule —— 通知Apache這是一條獨立的RewriteRule。
  • ^/pet-care/?$ —— 模式串,服務器會檢查每一條URL是否跟這個模式串匹配。若是匹配,Apache會使用後面的Substitution塊替換URL。
  • pet_care_info_01_02_2003.php —— Substitution。若是請求與前面的模式串匹配,則Apache會使用這個URL替換原來的URL。
  • [NC,L] —— 標記,告訴Apache如何應用規則。在本例中,咱們使用了兩個標記:「NC」告訴Apache這條規則不區分大小寫;「L」告訴Apache若是這條規則被應用,則再也不應用其餘規則。
  • # Handle requests for 「pet-care」 —— 註釋,用來解釋這條規則作了什麼(可選,可是建議這樣作)

這種規則是重寫一個獨立URL的簡單方式,也是幾乎全部URL重寫的基礎。

模式和替換

前面提到的規則使你能夠重定向單獨的URL,可是mod_rewrite的強大之處在於根據包涵的模式串,識別和重寫成組的URL。

如今咱們要把站點上全部的URL改爲像前面舉得例子中說的那樣。你如今的URL是這樣的:

XHTML

 

1

http://www.example.com/show_a_product.php?product_id=7

你想把它改爲這個樣子:

XHTML

 

1

http://www.example.com/products/7/

咱們能夠只寫一條規則來管理全部產品id,而不用對每一個id都寫一條規則。其實就是你想把這種類型的URL:

XHTML

 

1

http://www.example.com/show_a_product.php?product_id={a number}

改爲這種樣子的:

XHTML

 

1

http://www.example.com/products/{a number}/

爲了達到這個目的,你須要使用正則表達式。正則表達式是一種格式特殊的、方便服務器理解的pattern。一種典型的用來匹配數字的正則表達式像這樣:

 

1

[0-9]+

方括號內含有一系列的字符,「0-9」表示全部數字。加號表示將會匹配任何加號前出現的pattern——本例中也就是「一個或多個數字」——也就是咱們要在URL中尋找的東西。

整個的模式部分通常都會被當作正則表達式進行處理——你不須要啓動或激活它們。

 

1

RewriteRule ^products/([0-9]+)/?$ show_a_product.php?product_id=$1 [NC,L] # Handle product requests

首先應該注意的是用括號括起來的模式串,這樣咱們能夠在接下來的Substitution中使用被括號中pattern匹配的URL進行「back-reference」(向後引用)。Substitution中的「$1」告訴Apache將以前被括號裏面的pattern匹配到的URL字串放到這裏。你能夠有不少back-reference,他們按出現順序編號。

像上面的RewriteRule語句,將會使Apache把全部domain.com/products/{number}/的請求重定向到show_a_product.php?product_id={same number}。

正則表達式

本文要講的並非完整的正則表達式的指南。然而,須要注意的重點是整個pattern都會被當作正則表達式處理,要一直注意正則表達式中那些特殊的字符。

最典型的例子是在pattern中使用句號。在一個patter中,’.’ 表示「任意字符」,而不是一個普通的句號,因此當你想要匹配一個句號的時候,你須要對句號進行「轉義」——就是在它前面加一個特殊的符號,反斜槓,它會讓Apache將下一個字符當作普通字符處理。

好比,下面這條語句 ,將會匹配」rss1xml」、」rss-xml」這樣的URL:

 

1

RewriteRule ^rss.xml$ rss.php [NC,L] # Change feed URL

這樣作通常不會有什麼很嚴重的問題,可是對字符進行適當轉義的習慣對深刻學習正則表達式有好處。因此最好的寫法應該是這樣:

 

1

RewriteRule ^rss.xml$ rss.php [NC,L] # Change feed URL

這種狀況只適用於模式比配,而不能用於替換。此外還有其餘的字符(咱們稱之爲「元字符」)用於轉義:

  • . (任意字符)
  • *(零個或多個字符)
  • +(一個或多個字符)
  • {}(匹配至少、至多個字符)
  • ? (非貪婪限制符,跟在任何一個限制符後面表示該
  • ! (負向預查,出如今字符串頭表示從不匹配該字符串的位置開始匹配)
  • ^(匹配輸入字符串的開始位置,出如今範圍中表示負)
  • $(表示字符串結尾)
  • [](包含,默認是一個字符長度)
  • – (當在方括號中出現時表示範圍)
  • () (段域)
  • | (對兩個匹配條件進行邏輯或運算)
  • (將下一個字符轉義)

經過使用正則表達式,咱們能夠匹配任意URL而且重寫它們。如今回到咱們文章開頭提到的例子,咱們但願匹配並重寫這條URL:

XHTML

 

1

http://www.example.com/parrots/norwegian-blue/

咱們想要把這條URL翻譯成以下的格式交給服務器:

XHTML

 

1

http://www.example.com/get_product_by_name.php?product_name=norwegian-blue

咱們能夠用很簡單一條規則完成這個工做:

 

1

RewriteRule ^parrots/([A-Za-z0-9-]+)/?$ get_product_by_name.php?product_name=$1 [NC,L] # Process parrots

這個規則讓咱們能夠提取出URL中「parrot/」以後的任意字母、數字和連字符的組合([A-Za-z0-9-])(將連字符放在字符末尾,方括號的最後,使之被當作連字符處理,而不是分隔符),並將匹配到的產品名稱替換爲$1.

若是須要的話,咱們也可讓規則更普適,使得無論產品出如今哪一個目錄下,均可以發送給相同的腳本,就像:

 

1

RewriteRule ^[A-Za-z-]+/([A-Za-z0-9-]+)/?$ get_product_by_name.php?product_name=$1 [NC,L] # Process all products

像這樣,咱們將「parrots」替換爲任意字母和連字符的組合。如今這條規則能夠匹配任意在parrots目錄下或任何以一個或多個字母和連字符組成的名稱的目錄下的產品了。

修正符

修正符跟在重寫規則的最後,用來告知Apache如何解釋和處理規則。好比能夠告訴Apache處理規則時不區分大小寫,當遇到第一個匹配時終止匹配,或其餘更多的選項。修正符用逗號隔開,並寫在方括號裏。下面是一些修正符和他們的含義(關於這些符號有一份速查表,不須要所有記住)。

  • C (與下一條規則關聯)
  • CO(設置特殊Cookie)
  • E=var:value (設置環境變量var: value)
  • F (禁用URL,返回403HTTP狀態碼)
  • G (強制URL爲GONE,返回410HTTP狀態碼)
  • H=handler (設置handler)
  • L (代表當前規則是最後一條規則,中止分析之後規則的重寫)
  • N (從新從第一條規則開始運行重寫過程)
  • NC (不區分大小寫)
  • NE (不在輸出轉義特殊字符)
  • NS (只用於不是內部子請求)
  • P (強制使用代理轉發)
  • PT (移交給下一個處理器– 當使用多個處理器對url進行處理的狀況下使用,如mod_alias)
  • R (強制外部重定向)
  • R=301 (強制重定向到新URL)
  • QSA (追加請求字符串)
  • S=x (跳過接下來x個規則)
  • T=mime-type (強制指定MIME類型)

移動內容

 

 

1

RewriteRule ^article/?$ http://www.new-domain.com/article/ [R,NC,L] # Temporary Move

在修正符段添加R修正符能夠改變RewriteRule的工做方式。這種狀況下Apache會向瀏覽器返發送一條信息(一個HTTP頭),告訴瀏覽器內容已經被臨時移動到了替換塊中的URL處,而不是在內部進行URL重寫。替換塊內能夠是絕對的URL也能夠是相對的。HTTP頭中還包含了302代碼,說明移動是臨時的。

 

1

RewriteRule ^article/?$ http://www.new-domain.com/article/ [R=301,NC,L] # Permanent Move

若是移動是永久的,給「R」修飾符添加「=301」字段,Apache會告訴瀏覽器內容被永久移動。與默認的R修飾符不一樣的是,「R=301」也會使瀏覽器地址欄顯示新的地址。

這是最多見的一種URL重寫的方法來把內容移動到新的 URL(好比在本網站中就被普遍使用,當文章地址改變時,會將用戶帶到新的地址去)。

條件

重寫規則能夠在一個或多個重寫條件下進行,並且能夠串聯,藉此咱們能夠對一些請求只使用一部分重寫。就我我的而言,我最常把這個規則應用到子域或替代域,它能夠知足各類各樣的標準,不只僅是URL的。舉個例子:

 

1

RewriteCond %{HTTP_HOST} ^addedbytes.com [NC] RewriteRule ^(.*)$ http://www.addedbytes.com/$1 [L,R=301]

上面這條重寫規則重定向全部請求到「www.addedbytes.com」。若是沒有這個條件,這個規則將會產生一個循環,全部匹配的請求都會被送回給本身。規則的目的是隻重定向URL中缺乏「www」的請求,重寫條件能夠幫助咱們達成目的。

重寫條件和重寫規則的使用方法差很少。首先寫「RewriteCond」告訴mod_rewrite這一行定義了一個條件。接下來是TestString和測試的模式串。最後是方括號內的修正符,跟RewriteRule的寫法差很少。

TestString(條件語句的第二部分)能夠表示不少不一樣的東西。好比在上面的例子中,你能夠檢測被請求的域,能夠檢測用戶使用的瀏覽器,能夠檢測引用URL(一般用來防止盜鏈),檢測用戶的IP地址,或者檢測其餘的東西(參考「服務器變量」一節瞭解其工做方式)。

模式串跟RewriteRule中的差很少同樣,可是有幾個小的例外。若是模式串的開始是一個特殊的字符(在「異常」一節定義的),那麼模式串將不會被解釋成一個匹配模式。這意味着若是你想要在正則表達式中使用用」<「,」>」,或者連字符開頭的字符串,你得給他們加一個反斜線用來轉義。

重寫條件後面也能夠像重寫規則那樣加修正符,可是隻有兩個。「NC」跟RewriteRule同樣,告訴Apache處理條件時忽略大小寫。另外一個修正符是「OR」,若是你想在有一兩條條件匹配時就應用規則,而不是所有都知足,在第一條條件後加入「OR」修正符(只有兩個條件的狀況下),這樣只須要有其中一條知足,規則就會被應用。默認行爲是在多個條件下,只有全部都知足的狀況下才可以應用規則。

異常和特例

重寫條件有不少不一樣的方式進行檢測——並不須要當作正則表達式的模式串,雖然正則表達式很經常使用。下面是一些處理重寫條件的方法選項:

  • <Pattern(是否測試串比模式串小)
  • >Pattern(是否測試串比模式串大)
  • =Pattern(測試串和模式串是否相等)
  • -d (測試串是不是一個合法的目錄)
  • -f  (測試串是不是一個合法的文件)
  • -s (測試串是不是一個合法的文件且大小不爲0)
  • -l  (測試串文件是否存在且是一個符號連接)
  • -F  (經過subrequest來檢查某文件是否可訪問)
  • -U (經過subrequest來檢查URL是否合法且可訪問)

服務器變量

服務器變量是在重寫條件中能夠被檢測的一些項目。這使得你能夠根據全部的請求參數——包括瀏覽器標識、引用URL或其餘的字符串——來應用適當的規則。變量格式以下:

  • HTTP Headers
    • HTTP_USER_AGENT
    • HTTP_REFERER
    • HTTP_COOKIE
    • HTTP_FORWARDED
    • HTTP_HOST
    • HTTP_PROXY_CONNECTION
    • HTTP_ACCEPT
  • Connection Variables
    • REMOTE_ADDR
    • REMOTE_HOST
    • REMOTE_USER
    • REMOTE_IDENT
    • REQUEST_METHOD
    • SCRIPT_FILENAME
    • PATH_INFO
    • QUERY_STRING
    • AUTH_TYPE
  • Server Variables
    • DOCUMENT_ROOT
    • SERVER_ADMIN
    • SERVER_NAME
    • SERVER_ADDR
    • SERVER_PORT
    • SERVER_PROTOCOL
    • SERVER_SOFTWARE
  • Dates and Times
    • TIME_YEAR
    • TIME_MON
    • TIME_DAY
    • TIME_HOUR
    • TIME_MIN
    • TIME_SEC
    • TIME_WDAY
    • TIME
  • Special Items
    • API_VERSION
    • THE_REQUEST
    • REQUEST_URI
    • REQUEST_FILENAME
    • IS_SUBREQ

多規則應用

越複雜的網站,就會有越複雜的規則來管理。當規則產生衝突的時候,行爲是不肯定的。每每在添加一條新的規則以後,會出現一些莫名其妙的問題,好比根本不起做用。若是這條規則自己是沒有問題的,那多是以前有一條規則匹配到了這個URL,因此這條URL根本沒有被匹配到新加入的規則。

 

1

2

RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ get_product_by_name.php?category_name=$1&product_name=$2 [NC,L] # Process product requests

RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ get_blog_post_by_title.php?category_name=$1&post_title=$2 [NC,L] # Process blog posts

在這個例子中,產品頁面和blog頁面有不一樣的模式串,可是第二條規則將不會匹配到URL,由於全部能被匹配的模式都已經被第一條規則匹配到了。

解決這個問題的方式有不少。不少CMS(包括wordpress)經過在URL中增長一個表示請求類型的串來供規則匹配,好比:

 

1

2

RewriteRule ^products/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ get_product_by_name.php?category_name=$1&product_name=$2 [NC,L] # Process product requests

RewriteRule ^blog/([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ get_blog_post_by_title.php?category_name=$1&post_title=$2 [NC,L] # Process blog posts

你也能夠寫一個單獨的PHP頁面來處理全部請求,它能夠檢查URL的第二個部分是否能匹配上一個blog或者一個產品。我一般這樣作,雖然可能會給服務器帶來一些額外的負擔,可是他讓URL更加簡潔。

 

1

RewriteRule ^([A-Za-z0-9-]+)/([A-Za-z0-9-]+)/?$ get_product_or_blog_post.php?category_name=$1&item_name=$2 [NC,L] # Process product and blog requests

還能夠經過設計更精確的規則和對規則進行更合理的安排來解決這個問題。想象一個blog有兩個分類集——主題和發佈年份。

 

1

2

RewriteRule ^([A-Za-z0-9-]+)/?$ get_archives_by_topic.php?topic_name=$1 [NC,L] # Get archive by topic

RewriteRule ^([A-Za-z0-9-]+)/?$ get_archives_by_year.php?year=$1 [NC,L] # Get archive by year

上面這兩條規則會衝突。固然,年份只由四位數字組成,因此你能夠吧規則寫的更精確,這樣只有在主題名稱也是四位數字的時候纔會產生衝突。

 

1

2

RewriteRule ^([0-9]{4})/?$ get_archives_by_year.php?year=$1 [NC,L] # Get archive by year

RewriteRule ^([A-Za-z0-9-]+)/?$ get_archives_by_topic.php?topic_name=$1 [NC,L] # Get archive by topic

mod_rewrite

Apache的mod_rewrite模塊在大多Apache託管中是標配,若是你使用共享的託管服務,你不須要作什麼配置。可是若是你是在管理本身的空間,你須要啓用mod_rewrite模塊。若是你在使用Apache1,你得修改httpd.conf文件,去掉下面這行行首的」#」

 

1

#LoadModule rewrite_module modules/mod_rewrite.so #AddModule mod_rewrite.c

若是在類Debian發行版上用Apache2,你只須要使用一下命令並重啓Apache:

 

1

sudo a2enmod rewrite

其餘發行版或其餘平臺可能不太同樣。若是上面這兩種方法都不適用於你的系統,那就去google一下吧。可能須要修改Apache2的配置文件,把「rewrite」加入到APACHE_MODULES列表裏,或者要修改httpd.conf,實在不行就下載mod_rewrite的源碼本身編譯安裝。這些方法都不麻煩的。

ISAPI_Rewrite

ISAPI_Rewrite是IIS上一個基於mod_rewrite的插件,它跟mod_rewrite的功能差很少,並且還有一些高質量的ISAPI_Rewrite論壇用來交流釋疑。由於ISAPI_Rewrite是IIS上的,安裝也很是簡單。

ISAPI_Rewrite的規則默認寫在httpd.ini文件中,錯誤日誌在httpd.parse.errors文件中。

正斜槓

在實際中我常常被URL重寫中的正斜槓所困擾,無論在模式串中、RewriteRule的替換串中仍是RewriteCond的狀態中,都會困擾我。這多是因爲我常常面對不一樣的URL重寫引擎,然而我仍然建議你們——當一個規則無效時,先注意一下是否是正斜槓搞的鬼。我一般在mod_rewrite規則中避免使用斜槓,可是在ISAPI_Rewrite中會使用。

示例規則

把舊的域重定向到一個新的域:

 

1

RewriteCond %{HTTP_HOST} old_domain.com [NC] RewriteRule ^(.*)$ http://www.new_domain.com/$1 [L,R=301]

重定向缺乏「www」的請求(添加「www」):

 

1

RewriteCond %{HTTP_HOST} ^domain.com [NC] RewriteRule ^(.*)$ http://www.domain.com/$1 [L,R=301]

重定向全部含有「www」的網頁(去掉「www」):

 

1

RewriteCond %{HTTP_HOST} ^www.domain.com [NC] RewriteRule ^(.*)$ http://domain.com/$1 [L,R=301]

把舊頁面重定向到新頁面:

 

1

RewriteRule ^old-url.htm$ http://www.domain.com/new-url.htm [NC,R=301,L]

相關文章
相關標籤/搜索