要求比對器
要求比對器可用於根據各種條件篩選 (或分類) 要求。
語法
在 Caddyfile 中,指令後緊接的比對器代碼可以限制該指令的範圍。比對器代碼可以是下列形式之一
如果指令支援比對器,它會以 [<比對器>]
顯示在語法文件。比對器代碼通常是 可選的,以 [ ]
表示。如果省略比對器代碼,則與萬用字元比對器 (*
) 相同。
範例
此指令適用於 所有 HTTP 要求
reverse_proxy localhost:9000
這也是相同的 (這裡不需要 *
)
reverse_proxy * localhost:9000
但是此指令僅適用於路徑以 /api/
開頭的請求 路徑
reverse_proxy /api/* localhost:9000
若要比對路徑以外的任何內容,請定義 命名比對器,並使用 @name
參照它
@postfoo {
method POST
path /foo/*
}
reverse_proxy @postfoo localhost:9000
萬用字元比對器
萬用字元 (或「全域比對」) 比對器 *
比對所有請求,而且僅在需要比對器代碼時才需要。例如,如果您要提供給指令的第一個引數也剛好是路徑,它看起來就像路徑比對器!因此,您可以使用萬用字元比對器來消除歧義,例如
root * /home/www/mysite
否則,此比對器不常使用。我們通常建議在語法不需要時省略它。
路徑比對器
透過 URI 路徑進行比對是最常見的請求比對方式,因此比對器可以內嵌,如下所示
redir /old.html /new.html
路徑比對器代碼必須以正斜線 /
開頭。
路徑比對 預設為完全比對,而不是前置比對。您必須附加 *
才能進行快速前置比對。請注意,/foo*
會比對 /foo
和 /foo/
以及 /foobar
;您可能實際上需要 /foo/*
。
命名比對器
所有不是路徑或萬用字元比對器的比對器都必須是命名比對器。這是在任何特定指令外部定義的比對器,並且可以重複使用。
使用唯一名稱定義比對器可讓您更靈活,讓您可以將 任何可用的比對器 組合到一個集合中
@name {
...
}
或者,如果集合中只有一個比對器,您可以將它放在同一行
@name ...
然後,您可以透過將比對器指定為指令的第一個引數來使用它
directive @name
例如,此指令將 WebSocket 請求代理到 localhost:6001
,而將其他請求代理到 localhost:8080
。它比對標題欄位名稱為 Connection
包含 Upgrade
以及另一個名稱為 Upgrade
且值為 websocket
的欄位
example.com {
@websockets {
header Connection *Upgrade*
header Upgrade websocket
}
reverse_proxy @websockets localhost:6001
reverse_proxy localhost:8080
}
如果比對器集合僅包含一個比對器,則單行語法也可以使用
@post method POST
reverse_proxy @post localhost:6001
作為特殊情況,expression
比對器 可以在不指定其名稱的情況下使用,只要有一個 引號 引數 (CEL 表達式本身) 出現在比對器名稱之後
@not-found `{err.status_code} == 404`
與指令一樣,命名比對器定義必須放在使用它們的 網站區塊 內。
命名比對器定義構成一個比對器組。組中的比對器會以 AND 方式結合,亦即所有比對器都必須符合。例如,如果組中有 header
和 path
比對器,則兩個比對器都必須符合。
可以合併同種類型的多個比對器(例如,在同一個組中合併多個 path
比對器),使用布林代數(AND/OR),如下面的各個區段所述。
對於較複雜的布林比對邏輯,建議使用 expression
比對器 來撰寫 CEL 程式碼,它支援 and &&
、or ||
和 括號 ( )
。
標準比對器
可以在 各個比對器模組的說明文件 中找到完整的比對器說明文件。
可以透過下列方式比對要求
client_ip
client_ip <ranges...>
expression client_ip('<ranges...>')
透過客戶端 IP 位址。接受精確的 IP 或 CIDR 範圍。支援 IPv6 區域。
當 trusted_proxies
全域選項已設定時,最適合使用這個比對器,否則它會與 remote_ip
比對器作用相同。只有來自受信任代理伺服器的要求會在要求開始時解析其客戶端 IP;不受信任的要求會使用直接對等方的遠端 IP 位址。
作為捷徑,private_ranges
可用於比對所有私有 IPv4 和 IPv6 範圍。它與指定所有這些範圍相同:192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8 fd00::/8 ::1
每個命名比對器可以有多個 client_ip
比對器,而且它們的範圍會合併並以 OR 方式結合。
範例
比對來自私有 IPv4 位址的要求
@private-ipv4 client_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8
這個比對器通常與 not
比對器配對使用,以反轉比對。例如,要中斷來自公用 IPv4 和 IPv6 位址的所有連線(這是所有私有範圍的相反值)
example.com {
@denied not client_ip private_ranges
abort @denied
respond "Hello, you must be from a private network!"
}
在 CEL 程式碼 中,它會像這樣
@my-friends `client_ip('12.23.34.45', '23.34.45.56')`
expression
expression <cel...>
透過任何會傳回 true
或 false
的 CEL(通用程式語言) 程式碼。
可以在這些 CEL 程式碼中使用 Caddy 佔位符(或 Caddyfile 速記),因為它們會在 CEL 環境解譯之前經過預處理並轉換成常規 CEL 函式呼叫。
大多數其他要求比對器也可以在表達式中作為函式使用,這允許布林邏輯比外部表達式更靈活。請參閱每個比對器的文件,以了解 CEL 表達式中支援的語法。
為了方便,如果定義僅包含 CEL 表達式的命名比對器,則可以省略比對器名稱。CEL 表達式必須是 引號(建議使用反引號或 here 文件)。這讀起來很不錯
@mutable `{method}.startsWith("P")`
在這種情況下,假設為 CEL 比對器。
範例
比對方法以 P
開頭的要求,例如 PUT
或 POST
@methods expression {method}.startsWith("P")
比對處理常式傳回錯誤狀態碼 404
的要求,將與 handle_errors
指令 結合使用
@404 expression {err.status_code} == 404
比對路徑符合兩個不同正規表示式的要求;這只能使用表達式來寫入,因為 path_regexp
比對器通常只能在每個命名比對器中存在一次
@user expression path_regexp('^/user/(\w*)') || path_regexp('^/(\w*)')
或者相同,省略比對器名稱,並用 反引號 包裹,以便將其解析為單一令牌
@user `path_regexp('^/user/(\w*)') || path_regexp('^/(\w*)')`
您可以使用 here 文件語法 來撰寫多行 CEL 表達式
@api <<CEL
{method} == "GET"
&& {path}.startsWith("/api/")
CEL
respond @api "Hello, API!"
file
file {
root <path>
try_files <files...>
try_policy first_exist|smallest_size|largest_size|most_recently_modified
split_path <delims...>
}
file <files...>
expression `file({
'root': '<path>',
'try_files': ['<files...>'],
'try_policy': 'first_exist|smallest_size|largest_size|most_recently_modified',
'split_path': ['<delims...>']
})`
expression file('<files...>')
依檔案。
-
root
定義要尋找檔案的目錄。預設為目前工作目錄,或已設定的root
變數 ({http.vars.root}
)(可透過root
指令 設定)。 -
try_files
檢查其清單中符合 try_policy 的檔案。若要比對目錄,請在路徑後加上尾隨正斜線
/
。所有檔案路徑都相對於網站 根目錄,且 glob 模式 將會擴充。如果
try_policy
為first_exist
(預設),則清單中的最後一個項目可能是以=
為字首的數字(例如=404
),它作為後備,將發出具有該代碼的錯誤;該錯誤可以透過handle_errors
捕捉並處理。 -
try_policy
指定如何選擇檔案。預設為first_exist
。-
first_exist
檢查檔案是否存在。選擇第一個存在的檔案。 -
smallest_size
選擇大小最小的檔案。 -
largest_size
選擇檔案大小最大的檔案。 -
most_recently_modified
選擇最近修改的檔案。
-
-
split_path
會將路徑在每個檔案路徑中找到的第一個分隔符號處分割。對於每個分割值,分割的左側(包含分隔符號本身)將會是嘗試的檔案路徑。例如,/remote.php/dav/
使用分隔符號.php
會嘗試檔案/remote.php
。每個分隔符號都必須出現在 URI 路徑組件的結尾,才能用作分割分隔符號。這是一個利基設定,主要用於提供 PHP 網站時使用。
由於 try_files
採用 first_exist
政策非常常見,因此有一個單行捷徑
file <files...>
一個空的 file
比對器(後面沒有列出任何檔案)會查看請求的檔案(從 URI 原封不動地複製,相對於 網站根目錄)是否存在。這實際上與 file {path}
相同。
比對後,將提供四個新的佔位符
{file_match.relative}
檔案的根目錄相對路徑。這在改寫請求時通常很有用。{file_match.absolute}
比對檔案的絕對路徑,包括根目錄。{file_match.type}
檔案類型,file
或directory
。{file_match.remainder}
分割檔案路徑後剩下的部分(如果配置了split_path
)
範例
比對路徑為存在檔案的請求
@file file
比對路徑後接 .html
為存在檔案的請求,或如果不存在,則比對路徑為存在檔案的請求
@html file {
try_files {path}.html {path}
}
與上述相同,但使用單行捷徑,如果找不到檔案,則回退到發出 404 錯誤
@html-or-error file {path}.html {path} =404
更多使用 CEL 表達式 的範例。請記住,佔位符會在 CEL 環境解釋之前預處理並轉換為常規 CEL 函式呼叫,因此這裡使用串接。此外,由於當前的解析限制,如果與佔位符串接,則必須使用長格式
@file `file()`
@first `file({'try_files': [{path}, {path} + '/', 'index.html']})`
@smallest `file({'try_policy': 'smallest_size', 'try_files': ['a.txt', 'b.txt']})`
header
header <field> [<value>]
expression header({'<field>': '<value>'})
按請求標頭欄位。
<field>
是要檢查的 HTTP 標頭欄位的名稱。- 如果以
!
為前綴,則欄位必須不存在才能比對(省略值引數)。
- 如果以
<value>
是欄位必須具有的值才能比對。- 如果以
*
為前綴,則執行快速後綴比對(出現在結尾)。 - 如果以
*
為後綴,則執行快速前綴比對(出現在開頭)。 - 如果用
*
括起來,則執行快速子字串比對(出現在任何地方)。 - 否則,這是一個快速精確比對。
- 如果以
同一個組中的不同標頭欄位是 AND-ed。每個欄位的多個值是 OR-ed。
請注意,標頭欄位可能會重複並且具有不同的值。後端應用程式必須考慮標頭欄位值是陣列,而不是單數值,而 Caddy 也不會在這種兩難境地中詮釋意義。
範例
比對包含 Upgrade
的 Connection
標頭的請求
@upgrade header Connection *Upgrade*
比對包含 bar
或 baz
的 Foo
標頭的請求
@foo {
header Foo bar
header Foo baz
}
比對完全沒有 Foo
標頭欄位的請求
@not_foo header !Foo
使用 CEL 表達式,透過檢查包含 Upgrade
的 Connection
標頭和等於 websocket
的 Upgrade
標頭,來比對 WebSocket 請求
@websockets `header({'Connection':'*Upgrade*','Upgrade':'websocket'})`
header_regexp
header_regexp [<name>] <field> <regexp>
expression header_regexp('<name>', '<field>', '<regexp>')
expression header_regexp('<field>', '<regexp>')
與 header
類似,但支援正規表示式。擷取群組可透過 佔位符 存取,例如 {re.name.capture_group}
,其中 name
是正規表示式的名稱(可選,但建議使用),而 capture_group
是表達式中擷取群組的名稱或數字。擷取群組 0
是完整的正規表示式比對,1
是第一個擷取群組,2
是第二個擷取群組,依此類推。
使用的正規表示式語言是 RE2,包含在 Go 中。請參閱 RE2 語法參考 和 Go regexp 語法概觀。
每個標頭欄位僅支援一個正規表示式。多個不同的欄位將會 AND-ed。
範例
比對 Cookie 標頭包含 login_
後接十六進制字串的請求,並使用可透過 {re.login.1}
存取的擷取群組。
@login header_regexp login Cookie login_([a-f0-9]+)
或者使用 CEL 表達式 執行相同的操作
@login `header_regexp('login', 'Cookie', 'login_([a-f0-9]+)')`
host
host <hosts...>
expression host('<hosts...>')
根據請求的 Host
標頭欄位比對請求。
由於大多數網站區塊已在網站的地址中指出主機,因此此比對器更常使用在使用萬用字元主機名的網站區塊中(請參閱 萬用字元憑證模式),但需要主機名稱特定邏輯時。
多個 host
比對器將會 OR-ed 在一起。
範例
比對一個次網域
@sub host sub.example.com
比對頂層網域和子網域
@site host example.com www.example.com
使用 CEL 表達式 的多個子網域
@app `host('app1.example.com', 'app2.example.com')`
method
method <verbs...>
expression method('<verbs...>')
透過 HTTP 要求的方法 (動詞)。動詞應為大寫,例如 POST
。可以比對一種或多種方法。
多個 method
比對器會使用 OR 進行運算。
範例
比對使用 GET
方法的要求
@get method GET
比對使用 PUT
或 DELETE
方法的要求
@put-delete method PUT DELETE
使用 CEL 表達式 比對唯讀方法
@read `method('GET', 'HEAD', 'OPTIONS')`
not
not <matcher>
或者,若要否定會使用 AND 進行運算的多個比對器,請開啟一個區塊
not {
<matchers...>
}
封閉的比對器的結果會被否定。
範例
比對路徑不以 /css/
或 /js/
開頭的要求。
@not-assets {
not path /css/* /js/*
}
比對不具有下列任一條件的要求
/api/
路徑前綴,或POST
要求方法
亦即,必須不具備這些條件才能比對
@with-neither {
not path /api/*
not method POST
}
比對不具備下列兩者條件的要求
/api/
路徑前綴,且POST
要求方法
亦即,必須不具備任一條件才能比對
@without-both {
not {
path /api/*
method POST
}
}
此比對器沒有 CEL 表達式,因為您可以使用 !
算子進行否定。例如
@without-both `!path('/api*') && !method('POST')`
這與使用括號的表達式相同
@without-both `!(path('/api*') || method('POST'))`
path
path <paths...>
expression path('<paths...>')
透過要求路徑 (要求 URI 的路徑組成部分)。路徑比對是精確的,但大小寫不敏感。可以使用萬用字元 *
- 僅在結尾處,用於前綴比對 (
/prefix/*
) - 僅在開頭處,用於後綴比對 (
*.suffix
) - 僅在兩側,用於子字串比對 (
*/contains/*
) - 僅在中間,用於球狀比對 (
/accounts/*/info
)
斜線很重要。例如,/foo*
會比對 /foo
、/foobar
、/foo/
和 /foo/bar
,但 /foo/*
不會 比對 /foo
或 /foobar
。
要求路徑會經過清除,以在比對前解決目錄橫跨點。此外,除非比對模式有多個斜線,否則會合併多個斜線。換句話說,/foo
會比對 /foo
和 //foo
,但 //foo
只會比對 //foo
。
由於任何給定 URI 都有多種轉譯形式,因此要求路徑會經過正規化 (URL 解碼、取消轉譯),但比對模式中存在轉譯序列的位置除外。例如,/foo/bar
會比對 /foo/bar
和 /foo%2Fbar
,但 /foo%2Fbar
只會比對 /foo%2Fbar
,因為轉譯序列明確指定在組態中。
特殊通配符跳脫字元 %*
也可以用來取代 *
,以保持其比對區間的跳脫狀態。例如,/bands/*/*
將不會比對到 /bands/AC%2FDC/T.N.T
,因為路徑會在正規化的空間中進行比對,看起來像 /bands/AC/DC/T.N.T
,這不符合樣式;然而,/bands/%*/*
將會比對到 /bands/AC%2FDC/T.N.T
,因為由 %*
表示的區間將在不對跳脫序列進行解碼的情況下進行比對。
多個路徑將會透過 OR 運算子連結在一起。
範例
比對多個目錄及其內容
@assets path /js/* /css/* /images/*
比對特定檔案
@favicon path /favicon.ico
比對檔案副檔名
@extensions path *.js *.css
搭配 CEL 表達式
@assets `path('/js/*', '/css/*', '/images/*')`
path_regexp
path_regexp [<name>] <regexp>
expression path_regexp('<name>', '<regexp>')
expression path_regexp('<regexp>')
與 path
類似,但支援正規表示法。請在路徑的 URI 解碼/未跳脫形式上撰寫樣式。
使用的正規表示式語言是 RE2,包含在 Go 中。請參閱 RE2 語法參考 和 Go regexp 語法概觀。
擷取群組可透過 佔位符 存取,例如 {re.name.capture_group}
,其中 name
是正規表示法的名稱(可選,但建議使用),而 capture_group
是表達式中擷取群組的名稱或編號。擷取群組 0
是完整的正規表示法比對,1
是第一個擷取群組,2
是第二個擷取群組,以此類推。
每個命名比對器只能有一個 path_regexp
樣式。
範例
比對路徑以 6 個字元的十六進位字串結尾,並以 .css
或 .js
作為檔案副檔名的要求,並使用擷取群組,這些群組可分別透過 {re.static.1}
和 {re.static.2}
存取,每個部分都包含在 ( )
中。
@static path_regexp static \.([a-f0-9]{6})\.(css|js)$
或使用 CEL 表達式 執行相同的動作,同時驗證 file
是否存在於磁碟上
@static `path_regexp('static', '\.([a-f0-9]{6})\.(css|js)$') && file()`
protocol
protocol http|https|grpc|http/<version>[+]
expression protocol('http|https|grpc|http/<version>[+]')
依據要求通訊協定。可以使用廣泛的通訊協定名稱,例如 http
、https
或 grpc
;或特定的或最低的 HTTP 版本,例如 http/1.1
或 http/2+
。
每個命名比對器只能有一個 protocol
比對器。
範例
比對使用 HTTP/2 的要求
@http2 protocol http/2+
搭配 CEL 表達式
@http2 `protocol('http/2+')`
query
query <key>=<val>...
expression query({'<key>': '<val>'})
expression query({'<key>': ['<vals...>']})
依據查詢字串參數。應為 key=value
成對的序列。金鑰會進行完全比對(大小寫敏感),但也會支援 *
來比對任何值。值可以使用佔位符。
每個命名比對器可以有多個 query
比對器,具有相同金鑰的成對會透過 OR 運算子連結在一起。
非法的查詢字串(語法錯誤、未跳脫的分號等)將無法解析,因此不會比對。
注意:查詢字串參數是陣列,而不是單一值。這是因為查詢字串中允許多個金鑰,而且每個金鑰可能具有不同的值。如果查詢字串中指派了任何一個已設定的值,此比對器將會比對金鑰。使用查詢字串的後端應用程式必須考慮到查詢字串值是陣列,並且可以有多個值。
範例
比對具有任何值的 q
查詢參數
@search query q=*
比對具有值 asc
或 desc
的 sort
查詢參數
@sorted query sort=asc sort=desc
比對 q
和 sort
,搭配 CEL 表達式
@search-sort `query({'sort': ['asc', 'desc'], 'q': '*'})`
remote_ip
remote_ip [forwarded] <ranges...>
expression remote_ip('<ranges...>')
expression remote_ip('forwarded', '<ranges...>')
依據遠端 IP 位址(即直接對等方的 IP 位址)。接受精確的 IP 或 CIDR 範圍。支援 IPv6 區域。
作為捷徑,private_ranges
可用於比對所有私有 IPv4 和 IPv6 範圍。它與指定所有這些範圍相同:192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8 fd00::/8 ::1
⚠️ forwarded
選項已過時,並將在未來版本中移除。其實作並不安全。請改用 client_ip
比對器,它允許安全比對從 HTTP 標頭中解析出的真實客戶端 IP。如果啟用,則在 X-Forwarded-For
要求標頭中第一個 IP(如果存在)將優先作為參考 IP,而不是預設的直接對等 IP。
每個命名比對器可以有多個 remote_ip
比對器,且其範圍將合併並以 OR 運算。
範例
比對來自私有 IPv4 位址的要求
@private-ipv4 remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 127.0.0.1/8
這個比對器通常與 not
比對器配對使用,以反轉比對。例如,要中斷來自公用 IPv4 和 IPv6 位址的所有連線(這是所有私有範圍的相反值)
example.com {
@denied not remote_ip private_ranges
abort @denied
respond "Hello, you must be from a private network!"
}
在 CEL 程式碼 中,它會像這樣
@my-friends `remote_ip('12.23.34.45', '23.34.45.56')`
vars
vars <variable> <values...>
依據要求內容中的變數值,或佔位符的值。可以指定多個值來比對所有可能的那些值(以 OR 運算)。
<variable> 參數可以是變數名稱或大括號 { }
中的佔位符。(佔位符不會在第一個參數中展開。)
此比對器最適合與設定輸出的 map
指令 搭配使用,或與在要求內容中設定一些資訊的外掛程式搭配使用。
範例
比對名為 magic_number
的 map
指令 輸出的值 3
或 5
vars {magic_number} 3 5
vars_regexp
vars_regexp [<name>] <variable> <regexp>
類似於 vars
,但支援正規表示式。擷取群組可透過 佔位符 存取,例如 {re.name.capture_group}
,其中 name
是正規表示式的名稱(可選,但建議使用),而 capture_group
是表示式中擷取群組的名稱或數字。擷取群組 0
是完整的正規表示式比對,1
是第一個擷取群組,2
是第二個擷取群組,依此類推。
使用的正規表示式語言是 RE2,包含在 Go 中。請參閱 RE2 語法參考 和 Go regexp 語法概觀。
每個命名比對器只能有一個 vars_regexp
比對器。
範例
比對名為 magic_number
的 map
指令 輸出的值,其值以 4
開頭,並在擷取群組中擷取該值
vars_regexp magic {magic_number} ^(4.*)