返回文章列表

NGINX 模組化架構與 Rewrite 模組深度解析

本文探討 NGINX 的模組化架構,特別著重於 Rewrite 模組的應用與正規表示式的使用。文章涵蓋了 NGINX 模組的型別、Rewrite 模組的運作機制、正規表示式的基本語法、捕捉群組、命名捕捉以及如何在 NGINX 組態中實際應用正規表示式。此外,文章還闡述了 NGINX

Web 開發 系統管理

NGINX 的核心架構採用模組化設計,允許開發者根據需求選擇和組態不同的模組。這使得 NGINX 非常靈活,可以適應各種不同的應用場景。文章首先介紹了 NGINX 模組的分類別,包括預設啟用、編譯時啟用以及第三方模組。接著,重點探討了 Rewrite 模組的功能,說明如何利用正規表示式進行 URL 重寫,以及如何藉此提升網站的 SEO 和使用者經驗。更進一步地,文章詳細解釋了正規表示式的基本語法,包含各種元字元、數量詞以及捕捉群組的用法,並輔以實際案例說明如何在 NGINX 組態中有效運用這些技巧。最後,文章還區分了 NGINX 處理外部請求和內部請求的差異,並提供相關指令和組態範例,讓讀者更全面地理解 NGINX 的運作方式。

模組組態探索:NGINX的核心功能

NGINX的真正強大之處在於其模組系統。整個應用程式都是建立在模組化架構上,每個模組都可以在編譯時啟用或停用。有些模組提供簡單的功能,例如 autoindex 模組,它可以生成目錄中的檔案列表。而有些模組則會完全改變你對網頁伺服器的認知,例如 Rewrite 模組。除了現有的 NGINX 模組外,開發者還可以根據需要建立自己的模組。

在本章末尾,你可以找到第三方模組系統的快速概述。請注意,第三方模組由社群維護,並不保證這些模組會與你的 NGINX 版本相容。

本章涵蓋以下主題:

  • Rewrite 模組:不僅僅是重寫 URI
  • 預設 NGINX 版本中啟用的其他模組
  • 必須在編譯時啟用的可選模組
  • 第三方模組的簡要說明

深入探索 Rewrite 模組

Rewrite 模組為 NGINX 帶來了遠超過簡單指令集的功能。它定義了一個全新的請求處理層級,這一部分將在接下來的內容中詳細說明。

首先,這個模組(如名稱所暗示)用於進行 URL 重寫。這個機制允許你去除包含多個引數的醜陋 URL,例如 http://example.com/article.php?id=1234&comment=32。這些 URL 對一般訪客來說特別不具資訊量且無意義。

相反地,連結到你網站的 URL 將包含有用的資訊,指示即將存取的頁面性質。例如,URL http://website.com/article-1234-32-US-economy-strengthens.html 不僅對訪客更有趣,對搜尋引擎也更友好。URL 重寫是搜尋引擎最佳化(SEO)的關鍵元素。

這個機制的原理非常簡單:它是在接收到客戶請求後、提供檔案前重寫請求 URI。重寫後,URI 會與位置區塊進行比對,以找到應用於請求的組態。這項技術將在後續部分中詳細說明。

正規表示式回顧

首先,這個模組需要一定程度上的正規表示式(regex 或 regexp)理解。因為 URL 重寫是透過 rewrite 指令完成的,該指令接受一個模式後跟隨替換 URI。

這是一個廣泛的話題;有整本文專門解釋其奧妙。然而,我們即將檢視的簡化方法應該足夠讓你充分利用這個機制。

正規表示式的目的

我們首先要回答的問題是:正規表示式的目的是什麼?簡單來說,其主要目的是驗證一個字串是否比對給定模式。該模式使用特定語言來定義極其複雜且精確的規則:

| 字串 | 模式 | 是否比對 | 說明 | |


-|


|



|


| | hello | ^hello$ | 是 | 字串從 h 開始(^h),接著是 e, l, l ,然後以 o 結束(o$)。 | | hell | ^hello$ | 否 | 字串從 h 開始(^h),接著是 e, l, l ,但不以 o 結束。 | | Hello | ^hello$ | 未知 | 若執行比對的是區分大小寫引擎,則字串不比對該模式 |

表格4.1:包含說明的一些模式

當使用更複雜的模式時,這一概念變得更加有趣。例如:驗證電子郵件地址: ^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}$。程式化驗證一個合法電子郵件地址可能需要大量程式碼,而所有工作都可以透過單一正規表示式進行。

PCRE 語法

NGINX 使用的是 Perl Compatible Regular Expressions (PCRE) 函式庫語法(如果記得第2章),這對於自行構建 NGINX 是必需的一項前提條件,除非你停用使用它們的模組。它是最常見的一種正規表示式形式,幾乎所有你學到的是都適用於其他語言變體。

在最簡單形式下,一個模式由一個字元構成——例如 x。我們可以將字串與該模式進行比對。“example” 比對 x 模式嗎?是的,“example” 包含字元 “x”。它還可以是多個特定字元;[a-z] 模式比對 a 和 z 之間任意一個字元, 或甚至是字母和數字的混合: [a-z0-9]。因此, “hell[a-z0-9]” 模式驗證以下字串: “hello” 和 “hell4”,但不包括 “hell” 或 “hell!"。

你可能已經注意到我們使用了 [ 和 ] 段落符號,它們被稱為元字元, 對於讓語法更具指導性起著重要作用。總共有11種元字元,每個都扮演著不同角色。 若要建立實際包含其中之一元字元,必須逃避該段落 (\反斜槓):

| 元字元 | 說明 | |


-|


| | ^ | 開始
此字元後面必須是開頭:
• 範例段落: ^h
• 比對範例: hello, h, hh (任何以 h 開始)
• 不比對範例: character, ssh | | $ | 結果
此字元前面必須位於結尾:
• 範例段落: e$
• 比對範例: sample, e, file (任何以 e結尾)
• 不比對範例: extra, shell | | . (點) |任意
比對任何字元:
• 範例段落: hell.
• 比對範例: hello, hellx, hell5 和 hell!
• 不比對範例: hell 和 helo |

自行建立最適合需求之自定義 Rewrite 條件

玄貓認為解析清楚 Rewrite 的內容後就可以輕鬆地使用其指令進行適當調整。 Rewrite 條件可以根據 URL、HTTP Header、變數等多種條件進行判斷。 以下是幾個常見條件及其應用情境:

# 根據 URL 的條件
rewrite ^/old-path/(.*) /new-path/$1 break;
# 根據 HTTP Header 的條件
if ($http_user_agent ~* MSIE) {
    rewrite ^(.*)$ /ie/$1 break;
}
# 根據變數值判斷
set $my_var "value";
if ($my_var = "value") {
    rewrite ^(.*)$ /custom/$1 break;
}

內容解密:

上述程式碼展示瞭如何根據不同條件進行重寫操作。

  1. 第一段程式碼中,NGINX 檢查請求 URL 是否以 /old-path/ 開始。 如果比對成功則會將 /old-path/ 則替換為 /new-path/
  2. 第二段程式碼中檢查 HTTP Header 中 User-Agent 是否包含 MSIE。 如果比對成功則將所有請求轉向 /ie/ 下。
  3. 第三段程式碼中會先設定變數 $my_var 的值為 value。 然後再檢查 $my_var 是否等於 value。 如果條件成立則轉向 /custom/ 下。

玄貓認為只要稍微瞭解一下 Rewrite 指令和常見條件, 就能根據實際需求構建出最適合自己的 Rewrite 組態。 以上示範了幾種常見情境及其應對策略, 希望能夠幫助大家更好地掌握 Rewrite 的使用技巧, 進而提升網站管理和最佳化能力。

Plantuml

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title NGINX 模組化架構與 Rewrite 模組深度解析

package "正規表示式" {
    package "基本語法" {
        component [字元類 [abc]] as char_class
        component [量詞 * + ?] as quantifier
        component [錨點 ^ $] as anchor
    }

    package "進階功能" {
        component [群組 ()] as group
        component [後向參考 \1] as backref
        component [前瞻後顧] as lookahead
    }

    package "Python re 模組" {
        component [re.match()] as match
        component [re.search()] as search
        component [re.findall()] as findall
        component [re.sub()] as sub
    }
}

char_class --> quantifier : 重複匹配
quantifier --> anchor : 位置定位
group --> backref : 捕獲參考
match --> search : 模式搜尋
search --> findall : 全部匹配
findall --> sub : 取代操作

note right of lookahead
  (?=...) 正向前瞻
  (?!...) 負向前瞻
  (?<=...) 正向後顧
end note

@enduml

內容解密:

此圖示展示了 NGINX 在處理客戶端請求時如何透過 URI 轉向來決定應用哪些位置區塊。當客戶端發出請求時(A),NGINX 首先檢查 URL 是否與重寫規則比對(B)。如果比對成功(C),URI 會被轉向到新位置;如果未比對(D),則直接處理原始請求並應用相應位置區塊(E)。

NGINX 的 Rewrite 模組提供了靈活且強大的功能來處理和轉發 HTTP 請求。透過理解和掌握這些功能,我們可以實作更高效和可靠的網站伺服器管理和最佳化。玄貓希望透過深入瞭解和實踐 NGINX 的 Rewrite 模組功能來提升網站效能和使用者經驗。

NGINX 正規表示式與重寫模組深度探索

NGINX 是一個強大的網頁伺服器,其功能強大且靈活,特別是在處理 URL 重寫和正規表示式時。本文將探討 NGINX 中的正規表示式及其在重寫模組中的應用,並提供實際案例和詳細解說。

正規表示式元字元

正規表示式是一種強大的工具,能夠用來比對和操作文字。以下是一些常見的元字元及其在 NGINX 中的應用:

  • [ ]:字元集

    • 說明:比對指定集合中的任何字元。
    • 語法:[a-z] 表示比對任何小寫字母,[abcd] 表示比對 abcd[a-z0-9] 表示比對任何小寫字母或數字。
    • 範例模式:hell[a-y123-]
    • 比對字串:hellohell1hell2hell3hell-
    • 不比對字串:hellzhell4heloohe-llo
  • [^ ]:否定字元集

    • 說明:比對不在指定集合中的任何字元。
    • 語法:[^a-np-z0-9]
    • 比對字串:hellohell!
    • 不比對字串:hellahell5
  • |:替代

    • 說明:比對 | 前後的任一模式。
    • 語法:hello|welcome
    • 比對字串:hellowelcomehelloesawelcome
    • 不比對字串:helloelloeslo, ellow, owelcom.
  • ( ):分組

    • 說明:將多個模式分組,通常用於替代或捕捉。
    • 語法:^(hello|hi) there$
    • 比對字串:'hello there''hi there'
    • 不比對字串:'hey there''ahoy there'
  • \:轉義

    • 說明:用於轉義特殊字元,使其成為普通字元。
    • 語法:'Hello\\'
    • 比對字串:'Hello.' 不比對字串: ‘Hello’, ‘Hello!how are you?’

數量詞

數量詞允許您擴充套件接受的實體數量。以下是一些常見的數量詞及其應用:

  • *:0 次或多次

    • 說明: 前面的實體可以出現0次或多次
    模式: he*llo
    比對: hllo, hello, heeeello
    不比對: hallo, ello
    
  • +:1 次或多次

    模式: he+llo
    比對: hello, heeeello
    不比對: hllo, helo
    
  • ?****:0 次或1次

    模式: he?llo
    比對: hello, hllo
    不比對: heello, heeeello
    
  • {x}:x 次

    模式: he{3}llo
    比對: heeello, oh heeello there!
    不比對: hello, heello, heeeello
    
  • {x,}:至少 x 次

    模式: he{3,}llo
    比對: heeello, heeeeeeello
    不比對: hllo, hello, hello
    
  • {x,y}:x到y次**

    模式: he{2,4}llo
    比對: heello, heeello, heeeello.
    不比對: hello, heeeeello.
    

由於 NGINX 組態檔案語法中的大括號會與正規表示式中的大括號衝突,因此需要將包含大括號的模式放在引號中:

rewrite hel{2,}o /hello.php; # 無效的寫法

rewrite "hel{2,}o" /hello.php; # 有效的寫法

rewrite 'hel{2,}o' /hello.php; # 有效的寫法

捕捉群組

捕捉群組是正規表示式的一個強大功能,允許您捕捉子表示式並將其儲存在變數中。這些變數可以在後續指令中使用。

例如:

模式: ^(hello|hi) (sir|mister)$

比對字串: 'hello sir'

捕捉結果:
$1 = hello,
$2 = sir.
模式 : ^(.*)$
比對字串:'nginx rocks'
捕捉結果:
$1=nginx rocks.

捕捉命名

NGINX 支援使用命名捕捉來提高可讀性。例如:

模式 : ^/(?<folder>[^/]+)/(?<file>.*)$

url : '/admin/doc'

捕捉結果:
$folder = admin,
$file = doc.

在 NGINX 中應用正規表示式

在 NGINX 中,您可以在 location 塊中使用正規表示式來比對請求 URI。例如:

server {
server_name website.com;
location ~* ^/(downloads|files)/(.*)$ {
add_header Capture1 $1;
add_header Capture2 $2;
}
}

在這個例子中,當請求 URI 與正規表示式 /downloads/file.txt, /files/archive.zip, 或 /files/docs/report.doc.時會被捕捉到:

$1 = downloads or files,
$2 = file.txt or archive.zip or report.doc.

外部與內部請求

NGINX 做出了外部請求與內部請求之間的區別。外部請求直接來自客戶端並被比對可能的 location 塊。

server {
server_name website.com;
location = /document.html {
deny all; # 測試指令.
}
}

#這樣一來當客戶端請求 http://website.com/document.html.時就會被拒絕連線.

內部請求由 NGINX 本身觸發,並且需要具備特定的指令才能產生。例如:

error_page, index, rewrite, try_files,
add_before_body and add_after_body (from the addition module)
Server-Side Includes (SSI) command.