編程學習網 > 服務器 > Nginx > Apache 和 Nginx 下的 URL 重寫
2020
04-09

Apache 和 Nginx 下的 URL 重寫



URL 重寫和重定向

URL 重寫是將頁面映射到本站另一頁面, 而重定向則是將頁面映射到另一主機(域名). 其中臨時重定向(R=302)和永久重定向(R=301)都是親搜索引擎的, 是 SEO 的重要技術. 通過重定向, 瀏覽器和搜索引擎都意思到頁面發生了變化, 從而分別改變地址欄顯示的地址和更新搜索引擎索引。


Apache 中的 URL 重寫和重定向

啟用Apache 的 URL 重寫功能, 需要開啟mod_rewrite模塊.。然后在服務器配置文件或htaccess中修改服務配置:
AllowOverride all
Options FollowSysLinks
服務器配置文件和htaccess文件中都可以配置 URL 重寫,前者是服務器級別, 后者是目錄級別。

開啟重寫日志

2.4 以下版本

在Apache 服務器配置文件中追加:
RewriteLog "自定義日志路徑" # 設置日志等級 RewriteLogLevel 9
等級0 為關閉, 9 表示輸出最詳細信息。


2.4 及其以上版本

在Apache 服務器配置文件中追加:
LogLevel alert rewrite:trace3
其中trace3 可以替換為 trace1 到 trace8, 數值越大輸出的信息越詳細. 最后輸出的日志信息在apache_error.log文件中.
注意: 日志級別越高, 越影響服務器性能. 生產環境下應關閉重寫日志.

重寫指令

RewriteEngine

RewriteEngine: 是否啟用重寫引擎. On啟用, Off不啟用.

RewriteBase

RewriteBase: 設置重寫的基準目錄. 基準目錄的根目錄就是網站的根目錄.

RewriteCond

RewriteCond: 設置重寫條件.
語法: 
RewriteCond TestString CondPattern [flags]

RewriteCond 指令定義一條規則條件。

在一條 RewriteRule 指令前面可能會有一條或多條 RewriteCond 指令,只有 TestString 符合 CondPattern 時規則才被應用于當前URL處理。


1) TestString是一個純文本的字符串,除了包含普通的字符外,還可以包括下列的可擴展結構:

$N: RewriteRule 后向引用,其中(0 <= N <= 9) . $N引用緊跟在 RewriteCond 后面的 RewriteRule 中模板中的括號中的模板在當前 URL 中匹配的數據。

%N: RewriteCond 后向引用, 其中(0 <= N <= 9). %N引用最后一個 RewriteCond 的模板中的括號中的模板在當前 URL 中匹配的數據。

${mapname:key|default}:RewriteMap 擴展。


2) CondPattern是條件 pattern, 即一個應用于當前實例TestString 的正則表達式, 即 TestString 將會被計算然后與CondPattern 匹配. 作為一個標準的擴展正則式, CondPattern 有以下補充:

可以在模板串前增加一個!前綴,以表示不匹配模板。但并不是所有的 test 都可以加!前綴。

CondPattern中可以使用以下特殊變量:

>CondPattern: 大于, 將 CondPattern 當作一個普通字符串, 將它和 TestString 進行比較, 當T estString 的字符大于CondPattern 為真.=CondPattern: 等于, 將 CondPattern 當作一個普通字符串,將它和TestString進行比較, 當 TestString 與 CondPattern 完全相同時為真. 如果 CondPattern 只是 "" (兩個引號緊挨在一起) 此時需 TestString 為空字符串方為真

-d: 是否為目錄, 將 TestString 當作一個目錄名, 檢查它否存在以及是否是一個目錄.

-f: 是否是 regular file, 將 TestString 當作一個文件名, 檢查它是否存在以及是否是一個 regular 文件.

-s: 是否為長度不為0的 regular文件, 將 TestString 當作一個文件名, 檢查它是否存在以及是否是一個長度大于0的 regular 文件.

-l: 是否為 symbolic link, 將 TestString 當作一個文件名, 檢查它是否存在以及是否是一個 symbolic link.

-F: 通過 subrequest 來檢查某文件是否可訪問. 檢查 TestString 是否是一個合法的文件, 而且通過服務器范圍內的當前設置的訪問控制進行訪問. 這個檢查是通過一個內部 subrequest 完成的, 因此需要小心使用這個功能以降低服務器的性能.

-U: 通過 subrequest 來檢查某個 URL 是否存在. 檢查 TestString 是否是一個合法的URL, 而且通過服務器范圍內的當前設置的訪問控制進行訪問. 這個檢查是通過一個內部 subrequest 完成的, 因此需要小心使用這個功能以降低服務器的性能.


3) [flags]是第三個參數,多個標志之間用逗號分隔。

nocase|NC: 不區分大小寫. 在擴展后的 TestString 和 CondPattern 中, 比較時不區分文本的大小寫. 注意, 這個標志對文件系統和 subrequest 檢查沒有影響.

ornext|OR: 建立與下一個條件的或的關系. 默認的情況下,二個條件之間是AND的關系, 用這個標志將關系改為OR. 例如:

RewriteCond %{REMOTE_HOST} ^host1.* [OR] RewriteCond %{REMOTE_HOST} ^host2.* [OR] RewriteCond %{REMOTE_HOST} ^host3.* RewriteRule …

如果沒有[OR]標志,需要寫三個條件/規則。


RewriteRule

RewriteRule: 設置重寫規則.

語法
RewriteRule Pattern Substitution [flags].

1) Pattern是一個作用于當前 URL 的兼容 perl 的正則表達式. 這里的“當前”是指該規則生效時的 URL 的值。

2) Substitution是當原始 URL 與 Pattern 相匹配時, 用以替代(或替換)的字符串. (RewriteRule 中也會進行一次判斷, 被捕獲的 URL 是否與 Pattern相匹配)

3) Substitution還可以追加特殊標記[flags]作為 RewriteRule 指令的第三個參數。 Flags 是一個包含以逗號分隔的下列標記的列表:


1.redirect|R[=code] (強制重定向 redirect)
強制性執行一個外部重定向. 如果code沒有指定, 則默認302. 如果需要使用在 300-400 范圍內的其他響應代碼, 只需在此指定這個數值即可。

另外, 還可以使用下列符號名稱之一: temp(默認的), permanent, seeother.

注意: 在使用這個標記時,務必確保該替換字段是一個有效的 URL ! 并且要記住,如果希望停止重寫操作而立即重定向,則還需要使用L標記。


2.forbidden|F(強制 URL 為被禁止的 forbidden )

強制當前URL 為被禁止的, 立即反饋一個 403. 使用這個標記, 可以鏈接若干 RewriteConds 以有條件地阻塞某些 URL。


3.gone|G(強制 URL 為已廢棄的 gone )

強制當前URL 為已廢棄的, 立即反饋一個 410. 使用這個標記, 可以標明頁面已經被廢棄而不存在了。


4.proxy|P(強制為代理 proxy )
此標記使替換成分被內部強制為代理請求, 并立即(重寫規則處理立即中斷)把處理移交給代理模塊. 你必須確保此替換串是一個有效的, 能夠為 Apache 代理模塊所處理的 URI. 使用這個標記, 可以把某些遠程成分映射到本地服務器名稱空間, 從而增強了 ProxyPass 指令的功能。
注意: 要使用這個功能, 代理模塊必須編譯在Apache服務器中. 不能確定, 可以檢查httpd -l的輸出中是否有mod_proxy.c. 如果有, 則mod_rewrite可以使用這個功能; 如果沒有, 則必須啟用mod_proxy并重新編譯httpd程序.


5.last|L(最后一個規則 last)
立即停止重寫操作, 并不再應用其他重寫規則. 它對應于 Perl中 的last命令或C語言中的break命令. 這個標記可以阻止當前已被重寫的 URL 為其后繼的規則所重寫.


6.next|N(重新執行 next round)
重新執行重寫操作(從第一個規則重新開始). 這時再次進行處理的URL已經不是原始的URL, 而是經最后一個重寫規則處理的URL. 它對應于 Perl 中的next命令或C語言中的continue命令. 此標記可以重新開始重寫操作, 即立即回到循環的頭部.


7.chain|C(與下一個規則相鏈接 chained)
此標記使當前規則與下一個(其本身又可以與其后繼規則相鏈接的, 并可以如此反復的)規則相鏈接. 它產生這樣一個效果: 如果一個規則被匹配, 通常會繼續處理其后繼規則, 即這個標記不起作用; 如果規則不能被匹配, 則其后繼的鏈接的規則會被忽略. 比如, 在執行一個外部重定向時, 對一個目錄級規則集, 你可能需要刪除".www" (此處不應該出現".www"的).


8.type|T=MIME-type(強制MIME類型 type)
強制目標文件的MIME 類型為 MIME-type. 比如, 它可以用于模擬 mod_alias 中的 ScriptAlias 指令, 在內部強制被映射目錄中的所有文件的 MIME 類型為"application/x-httpd-cgi".


9.nosubreq|NS(僅用于不對內部子請求進行處理 no internal sub-request)
在當前請求是一個內部子請求時, 此標記強制重寫引擎跳過該重寫規則. 比如, 在 mod_include 試圖搜索可能的目錄默認文件(http://index.xxx)時, Apache 會在內部產生子請求. 對子請求, 它不一定有用的, 而且如果整個規則集都起作用, 它甚至可能會引發錯誤. 所以, 可以用這個標記來排除某些規則.


10.nocase|NC(忽略大小寫 no case)
它使Pattern 忽略大小寫。


11.qsappend|QSA(追加請求串 query string append)

此標記強制重寫引擎在已有的替換串中追加一個請求串, 而不是簡單的替換. 如果需要通過重寫規則在請求串中增加信息, 就可以使用這個標記。


12.noescape|NE(在輸出中不對URI作轉義 no URI escaping)

此標記阻止mod_rewrite 對重寫結果應用常規的 URI 轉義規則. 一般情況下, 特殊字符(如%, $, ;等)會被轉義為等值的十六進制編碼. 此標記可以阻止這樣的轉義, 允許百分號等符號出現在輸出中,如:
RewriteRule /foo/(.*) /bar?arg=P1=$1 [R,NE] 可以使’/foo/zed’轉向到一個安全的請求’/bar?arg=P1=zed’.


13.passthrough|PT(移交給下一個處理器 pass through)

讓mod_rewrite 模塊將重寫的 URL 傳回給 Apache 做進一步處理。


14.skip|S=num(跳過后繼的規則 skip)

如果滿足某指定的條件,則跳過后面第num 調規則。


15.env|E=variable:value(設置環境變量 environment variable)

此標記使環境變量variable 的值為 value, VAL可以包含可擴展的反向引用的正則表達式$N和%N. 此標記可以多次使用以設置多個變量。這些變量可以在后繼的 RewriteCond 指令的pattern 中通過%{ENV:VAR}作引用 。


16.cookie|CO=NAME:VAL:domain[:lifetime[:path]](設置cookie)

在客戶端瀏覽器上設置一個cookie. cookie 的名稱是 NAME, 值是 VAL. domain字段是該 cookie 的域, 比如'.apache.org', 可選的 lifetime 是 cookie 生命期的分鐘數, 可選的 path 是 cookie 的路徑。

attachments-2020-04-5a3z3CG05e8e82e678784.jpg

在.htaccess 文件中配置重寫規則

在.htaccess文件中使用重寫功能時, RewriteRule 負責匹配的 URI 是相對.htaccess所在的目錄而言的。
例如訪問http://example.com/subdir1/subdir2/subdir3:
  • 如果.htaccess在網站根目錄下, 那么RewriteRule捕獲的 URI 是subdir1/subdir2/subdir3.
  • 如果.htaccess在 subdir1 目錄下, RewriteRule捕獲的 URI 是subdir2/subdir3.

RewriteRule重寫 URI 后的基準目錄也是以.htaccess所在的目錄為準. 例如: 訪問 http://example.com/foo

RewriteRule ^foo$ bar.php [L]
如果.htaccess在根目錄下, 重寫后訪問 http://example.com/bar.php. 如果在 subdir1 目錄下, 重寫后訪問 http://example.com/subdir1/bar.php.

例1:

<IfModule mod_rewrite.c># 啟用rewrite引擎RewriteEngine On# 重寫規則: 匹配任意以htm后綴的文件, 將htm替換成php. ^(.*)\.htm$ 是一個正則表達式, 表示需要重寫的部分, 此處指以任意字符開頭, 以.htm結尾的部分. $1.php 是一個重寫規則, $1 表示匹配到正則表達式中第一個子模式的字符串. [NC]: 表示重寫規則如何應用, 該處表示不區分大小寫. 整條規則即重寫以任意字符開頭, 以.htm結尾的部分, 重寫為由匹配到的第一個子模式字符串和.php拼接成的字符串。RewriteRule ^(.*)\.htm$ $1.php [NC]</IfModule>

IfModule 命令用于判斷是否安裝了 mod_rewrite 模塊。


例2:
<IfModule mod_rewrite.c>RewriteEngine On# 設置目錄級重寫的基準URIRewriteBase /subdir1/RewriteRule ^(.*)\.htm$ $1.php [NC,L,R]</IfModule>
  • RewriteBase設置了重寫的基準目錄. 如果上例中.htaccess位于網站根目錄下, 訪問的 http://example.com/foo.htm, 原本重寫后的基準目錄是網站根目錄/, 設置了RewriteBase后變為/subdir1/, 重寫后實際訪問 http://example.com/subdir1/foo.php.
  • 規則標志L: 表示如果可以匹配本條規則, 則不再繼續往下匹配.
  • 規則標志R: 表示臨時重定向, 即 302, 相當于[R=302]。


例3:
<IfModule mod_rewrite.c><IfModule mod_rewrite.c>RewriteEngine OnRewriteCond %{HTTP_HOST} (localhost:81)RewriteRule ^(.*) http://localhost:82 [R=301]</IfModule>
  • [R=301]: 表示永久性重定向, 用戶發送的所有請求都會被發送到新域名處理,永久重定向到另一個域名使用了絕對 URI, 因此不用再設置 RewriteBase。
  • %{HTTP_HOST}:表示引用 Apache 服務器變量 HTTP_HOST。
  • 該條RewriteCond 表示判斷 HTTP_HOST 是否是 localhost:81。

在httpd.conf 中配置重寫規則

在服務器配置文件中配置URL 重寫的指令與 .htaccess 文件中的格式相同,需要寫在<Directory>內.


Nginx

Nginx 的 URL 重寫功能不需要額外的支持. Nginx 本身只支持在服務器配置文件中配置 URL 重寫規則, 不支持 .htaccess 文件. 但可以通過在服務器配置文件中引入外部文件的方式, 實現對 .htaccess 文件的支持. 這會嚴重影響 Nginx 的性能。


重寫指令

rewrite

根據重寫規則, 重寫 URL。
語法:
 rewrite regex replacement flag
應用環境: server, location, if
可應用的flag 標記包括:
  • last: 相當于 Apache 中的L標記。
  • break: 終止匹配, 不再匹配后面的規則。
  • redirect: 臨時重定向, 返回 302。
  • permanent: 永久性重定向, 返回 301。
示例:
rewrite ^(.*)\.html$ $1.php last;
注意: 如果訪問的是 http://localhost/dir/file, rewrite 中的正則表達式匹配的是 /dir/file, 包括最左邊的正斜杠.


if 指令

語法: 
if (condition){...}
注意if之后緊跟的空格不能省略。
應用環境: server, location
判斷一個條件是否符合, 如果符合, 則執行大括號內語句. if 指令不支持嵌套, 不支持&&和||操作。

condition中可以包含如下判斷表示:

  • ~: 區分大小寫進行匹配
  • ~*: 不區分大小寫進行匹配
  • !~: 區分大小寫進行非匹配
  • !~*: 不區分大小寫進行非匹配
  • -f: 如果文件存在
  • !-f: 如果文件不存在
  • -d: 如果目錄存在
  • !-d: 如果目錄不存在
  • -e: 如果文件或目錄存
  • !-e: 如果文件或目錄不存在
  • -x: 如果文件可執行
  • !-x: 如果文件不可執行
示例:
location ~ \.html$ {if (!-f $request_filename){rewrite (.*)\.html$ $1.php last;}}
訪問的URL 以.html結尾時, 判斷是否存在該文件, 如果不存在就重寫為訪問同名的 PHP 腳本。


return 指令

語法:
 return code復制代碼
應用環境: server, location, if
該指令用于結束規則的執行并返回狀態碼給客戶端。
例如:
return 403;


location 指令

語法: 
location [flags] regex
應用環境: server

flags 包括:

  • ~: 執行一個正則匹配, 區分大小寫
  • ~*: 執行一個正則匹配, 不區分大小寫
  • ^~: 表示普通字符匹配. 使用前綴匹配成功. 如果匹配成功, 則不再匹配其他 location.
  • =: 進行普通字符完全匹配.
優先級:
  • =的優先級最高. 匹配成功后, 不再匹配其他項.
  • ^~類型表達式. 匹配成功后, 不再匹配其他項.
  • ~和~*. 如果有多個 location 的正則匹配.
  • 常規字符串匹配類型, 按前綴匹配.


rewrite_log 指令

語法: 
rewrite_log on|off
應用環境: server, location, if
啟用時, 將在error_log中記錄 notice 級別的重寫日志.


set 指令

語法:
set variable value
應用環境: server, location, if
為給定的變量設置一個特定值。


Nginx 內置的全局變量

$args: 這個變量等于請求行中的參數, 同 $query_string.
  • $content_length: 請求頭中的 Content-length 字段.
  • $content_type: 請求頭中的 Content-Type 字段.
  • $document_root: 當前請求在 root 指令中指定的值.
  • $host: 請求主機頭字段,否則為服務器名稱.
  • $http_user_agent: 客戶端 agent 信息.
  • $http_cookie: 客戶端 cookie 信息.
  • $limit_rate: 這個變量可以限制連接速率.
  • $request_method: 客戶端請求的動作,通常為 GET 或 POST.
  • $remote_addr: 客戶端的 IP 地址.
  • $remote_port: 客戶端的端口.
  • $remote_user: 已經經過 Auth Basic Module 驗證的用戶名.
  • $request_filename: 當前請求的文件路徑, 由 root 或 alias 指令與 URI 請求生成.
  • $scheme: HTTP 方法(如 http, https).
  • $server_protocol: 請求使用的協議, 通常是HTTP/1.0 或 HTTP/1.1.
  • $server_addr: 服務器地址, 在完成一次系統調用后可以確定這個值.
  • $server_name: 服務器名稱.
  • $server_port: 請求到達服務器的端口號.
  • $request_uri: 包含請求參數的原始 URI, 不包含主機名, 如: ”/foo/bar.php?arg=baz”.
  • $uri: 不帶請求參數的當前 URI, $uri 不包含主機名, 如 "/foo/bar.html".
  • $document_uri: 與$uri相同.

掃碼二維碼 獲取免費視頻學習資料

Python編程學習

查 看2022高級編程視頻教程免費獲取