Caddyfile 概念 本文件將幫助你詳細了解 HTTP Caddyfile。 結構 區塊 指令 標記與引號 全域選項 位址 Matcher Placeholder Snippet 命名路由 註釋 環境變數 結構 Caddyfile 的結構可以用視覺化方式描述: { email you@yours.com servers { trusted_proxies static private_ranges } } (snippet) { # this is a reusable snippet log { output file /var/log/access.log } } example.com { @post { method POST } reverse_proxy @post localhost:9001 localhost:9002 { lb_policy first } file_server /static import snippet } www.example.com { redir https://example.com{uri} import snippet } 圖例 全域選項區塊 Snippet 網站區塊 Matcher 定義 選項名稱 選項值 註釋 網站位址 指令 Matcher 標記 參數 子指令 關鍵點: 一個可選的 全域選項區塊 可以是文件中的第一項。 Snippet 或 命名路由 可以選擇出現在其後。 否則,Caddyfile 的第一行 永遠 是要提供服務的網站 位址。 所有 指令 和 matcher 必須 放在網站區塊中。網站區塊之間沒有全域作用域或繼承關係。 如果只有一個網站區塊,其大括號 { } 是可選的。 一個 Caddyfile 由至少一個或多個網站區塊組成,這些區塊始終以一個或多個網站 位址 開始。出現在位址之前的任何指令都會使解析器感到困惑。 區塊 開啟和關閉 區塊 是使用大括號完成的: ... { ... } 左大括號 { 必須位於行尾,且前面有一個空格。 右大括號 } 必須單獨佔一行。 當只有一個網站區塊時,大括號(和縮排)是可選的。這是為了方便快速定義單個網站,例如,這個: localhost reverse_proxy /api/* localhost:9001 file_server 相當於: localhost { reverse_proxy /api/* localhost:9001 file_server } 當你只有一個網站區塊時;這純粹是個人偏好問題。 要使用同一個 Caddyfile 配置多個網站,你 必須 在每個網站周圍使用大括號來分隔它們的配置: example1.com { root /www/example.com file_server } example2.com { reverse_proxy localhost:9000 } 如果一個請求匹配多個網站區塊,則選擇具有最精確匹配位址的網站區塊。請求不會級聯到其他網站區塊。 指令 指令 是功能關鍵字,用於自定義服務網站的方式。它們 必須 出現在網站區塊中。例如,一個完整的文件伺服器配置可能如下所示: localhost { file_server } 或者是反向代理: localhost { reverse_proxy localhost:9000 } 在這些示例中,file_server 和 reverse_proxy 是指令。指令是網站區塊中一行的第一個單詞。 在第二個示例中,localhost:9000 是一個 參數,因為它出現在指令之後的同一行中。 有時指令可以開啟自己的區塊。子指令 出現在指令區塊內每行的開頭: localhost { reverse_proxy localhost:9000 localhost:9001 { lb_policy first } } 這裡,lb_policy 是 reverse_proxy 的子指令(它設置後端之間使用的負載平衡策略)。 除非另有說明,否則指令不能在其他指令區塊內使用。 例如,basic_auth 不能在 file_server 內使用,因為文件伺服器不知道如何進行身份驗證;但你可以在 route、handle 和 handle_path 區塊內使用指令,因為它們是專門為指令分組而設計的。 請注意,當 HTTP Caddyfile 被轉換時,HTTP handler 指令會根據特定的預設 指令順序 進行排序(除非在 route 區塊中),因此指令出現的順序無關緊要,除了在 route 區塊中。 標記與引號 Caddyfile 在解析之前會被分解為標記 (token)。空格在 Caddyfile 中非常重要,因為標記是由空格分隔的。 通常,指令期望一定數量的參數;如果單個參數的值包含空格,它將被解析為兩個單獨的標記: directive abc def 這可能會產生問題並回傳錯誤或意外行為。 如果 abc def 應該是單個參數的值,則需要將其用引號括起來: directive "abc def" 如果你需要在帶引號的標記中使用引號,引號也可以被轉義: directive "\"abc def\"" 為了避免轉義引號,你可以改用反引號 ` ` 來包裹標記;例如: directive `{"foo": "bar"}` 在帶引號的標記內,所有其他字符都按原樣處理,包括空格、製表符和換行符。因此,多行標記是可能的: directive "first line second line" 也支持 Heredocs : example.com { respond <<HTML <html> <head><title>Foo</title></head> <body>Foo</body> </html> HTML 200 } 起始 heredoc 標記必須以 << 開頭,後跟任何文本(建議使用大寫字母)。結束 heredoc 標記必須是相同的文本(在上面的示例中為 HTML)。如果需要,可以使用 \<< 轉義起始標記以防止 heredoc 解析。 結束標記可以縮排,這會導致每行文本都去除相應數量的縮排(靈感來自 PHP),這有利於區塊內部的可讀性,同時可以很好地控制標記文本中的空格。尾隨換行符也會被去除,但可以通過在結束標記之前添加一個額外的空行來保留。 其他標記可以跟在結束標記之後作為指令的參數(例如在上面的示例中,狀態碼 200)。 全域選項 Caddyfile 可以選擇以一個沒有鍵的名為 全域選項區塊 的特殊區塊開始: { ... } 如果存在,它必須是配置中的第一個區塊。 它用於設置全域應用的選項,或者不特別針對任何一個網站的選項。在內部,只能設置全域選項;你不能在其中使用常規網站指令。 例如,要啟用 debug 全域選項,該選項通常用於生成詳細日誌以進行故障排除: { debug } 閱讀全域選項頁面 以了解更多資訊。 位址 位址始終出現在網站區塊的頂部,通常是 Caddyfile 中的第一項。 以下是有效位址的示例: 位址 效果 example.com 使用受管理的 公信認證證書 的 HTTPS *.example.com 使用受管理的 通配符公信認證證書 的 HTTPS localhost 使用受管理的 本地信任證書 的 HTTPS http:// HTTP 全匹配,受 http_port 影響 https:// HTTPS 全匹配,受 https_port 影響 http://example.com 明確的 HTTP,帶有 Host matcher example.com:443 由於匹配 https_port 預設值而使用的 HTTPS :443 由於匹配 https_port 預設值而使用的 HTTPS 全匹配 :8080 非標準端口上的 HTTP,無 Host matcher localhost:8080 非標準端口上的 HTTPS,因為具有有效的網域 https://example.com:443 HTTPS,但同時擁有 https:// 和 :443 是多餘的 127.0.0.1 HTTPS,帶有本地信任的 IP 證書 http://127.0.0.1 HTTP,帶有 IP 位址 Host matcher(拒絕 localhost) 從位址中,Caddy 可能可以推斷出網站的方案、主機和端口。如果位址沒有端口,Caddyfile 將選擇與方案匹配的端口(如果已指定),否則將假定預設端口 443。 如果你指定了主機名,則只有具有匹配 Host 標頭的請求才會被接受。換句話說,如果網站位址是 localhost,那麼 Caddy 將不會匹配對 127.0.0.1 的請求。 可以使用通配符 (*),但僅用於精確表示主機名的一個標籤。例如,*.example.com 匹配 foo.example.com 但不匹配 foo.bar.example.com,而 * 匹配 localhost 但不匹配 example.com。有關實際示例,請參見 通配符證書模式。 要匹配所有主機,請省略位址的主機部分,例如簡單的 https://。這在當你事先不知道網域,使用 On-Demand TLS 時非常有用。 如果多個網站共享相同的定義,你可以將它們全部列在一起,並用空格和逗號分隔(至少需要一個空格)。以下三個示例是等效的: # 逗號分隔的網站位址 localhost:8080, example.com, www.example.com { ... } 或 # 空格分隔的網站位址 localhost:8080 example.com www.example.com { ... } 或 # 逗號和換行符分隔的網站位址 localhost:8080, example.com, www.example.com { ... } 位址必須是唯一的;你不能多次指定相同的位址。 Placeholder 不能 用於位址,但你可以在其中使用 Caddyfile 風格的 環境變數: {$DOMAIN:localhost} { ... } 預設情況下,網站綁定在所有網路介面上。如果你希望覆蓋此行為,請使用 bind 指令 或 default_bind 全域選項 來執行此操作。 Matcher HTTP handler 指令 預設應用於所有請求(除非另有說明)。 請求 matcher 可用於按給定標準對請求進行分類。使用 matcher,你可以精確指定某個指令適用於哪些請求。 對於支持 matcher 的指令,指令後的第一個參數是 matcher 標記。以下是一些示例: root * /var/www # matcher 標記:* root /index.html /var/www # matcher 標記:/index.html root @post /var/www # matcher 標記:@post Matcher 標記可以完全省略以匹配所有請求;例如,如果下一個參數看起來不像路徑 matcher,則不需要提供 *。 閱讀請求 matcher 頁面 以了解更多資訊。 Placeholder Placeholder 是將動態值注入靜態配置的一種簡單方法。它們可以用作指令和子指令的參數。 Placeholder 兩邊由大括號 { } 包圍,內部包含識別符,例如:{foo.bar}。起始 placeholder 大括號可以被轉義 \{like.this} 以防止替換。Placeholder 識別符通常使用點分隔命名空間,以避免跨模組衝突。 哪些 placeholder 可用取決於上下文。並非所有 placeholder 在配置的所有部分都可用。例如,HTTP 應用程式設置的 placeholder 僅在配置中與處理 HTTP 請求相關的部分可用(即在 HTTP handler 指令 和 matcher 中,但 不 在 tls 配置 中)。某些指令或 matcher 也可能設置自己的 placeholder,這些 placeholder 可以被其後的任何內容使用。某些 placeholder 是全域可用的。 你可以在 Caddyfile 中使用任何 placeholder,但為了方便起見,你也可以使用其中一些等效的簡寫,這些簡寫在解析 Caddyfile 時會被展開: Caddyfile 替換為 {cookie.*} {http.request.cookie.*} {client_ip} {http.vars.client_ip} {dir} {http.request.uri.path.dir} {err.*} {http.error.*} {file_match.*} {http.matchers.file.*} {file.base} {http.request.uri.path.file.base} {file.ext} {http.request.uri.path.file.ext} {file} {http.request.uri.path.file} {header.*} {http.request.header.*} {host} {http.request.host} {hostport} {http.request.hostport} {labels.*} {http.request.host.labels.*} {method} {http.request.method} {orig_method} {http.request.orig_method} {orig_uri} {http.request.orig_uri} {orig_path} {http.request.orig_uri.path} {orig_dir} {http.request.orig_uri.path.dir} {orig_file} {http.request.orig_uri.path.file} {orig_query} {http.request.orig_uri.query} {orig_?query} {http.request.orig_uri.prefixed_query} {path.*} {http.request.uri.path.*} {path} {http.request.uri.path} {%path} {http.request.uri.path_escaped} {port} {http.request.port} {query.*} {http.request.uri.query.*} {query} {http.request.uri.query} {%query} {http.request.uri.query_escaped} {?query} {http.request.uri.prefixed_query} {re.*} {http.regexp.*} {remote_host} {http.request.remote.host} {remote_port} {http.request.remote.port} {remote} {http.request.remote} {rp.*} {http.reverse_proxy.*} {resp.*} {http.intercept.*} {scheme} {http.request.scheme} {tls_cipher} {http.request.tls.cipher_suite} {tls_client_certificate_der_base64} {http.request.tls.client.certificate_der_base64} {tls_client_certificate_pem} {http.request.tls.client.certificate_pem} {tls_client_fingerprint} {http.request.tls.client.fingerprint} {tls_client_issuer} {http.request.tls.client.issuer} {tls_client_serial} {http.request.tls.client.serial} {tls_client_subject} {http.request.tls.client.subject} {tls_version} {http.request.tls.version} {upstream_hostport} {http.reverse_proxy.upstream.hostport} {uri} {http.request.uri} {%uri} {http.request.uri_escaped} {vars.*} {http.vars.*} 並非所有配置欄位都支持 placeholder,但在你期望的大多數地方都支持。對 placeholder 的支持需要被明確添加到這些欄位中。外掛作者可以 閱讀這篇文章 來了解如何在他們自己的模組中添加對 placeholder 的支持。 Snippet 你可以通過給特殊的區塊起一個用括號括起來的名稱來定義它們,稱為 snippet: (logging) { log { output file /var/log/caddy.log format json } } 然後你可以在任何需要的地方重複使用它,使用特殊的 import 指令: example.com { import logging } www.example.com { import logging } import 指令也可以用於在其位置包含其他文件。如果參數與定義的 snippet 不匹配,則將其作為文件嘗試。它還支持使用通配符導入多個文件。作為特殊情況,它可以出現在 Caddyfile 中的任何位置(除了作為另一個指令的參數),包括網站區塊之外: { email admin@example.com } import sites/* 你可以將參數傳遞給導入的配置(snippet 或文件),並像這樣使用它們: (snippet) { respond "Yahaha! You found {args[0]}!" } a.example.com { import snippet "Example A" } b.example.com { import snippet "Example B" } ⚠️ 實驗性 | v2.9.x+ 你還可以將可選區塊傳遞給導入的 snippet,並按如下方式使用它們。 (snippet) { {block} respond "OK" } a.example.com { import snippet { header +foo bar } } b.example.com { import snippet { header +bar foo } } 閱讀 import 指令頁面 以了解更多資訊。 命名路由 ⚠️ 實驗性 命名路由使用類似於 snippet 的語法;它們是在網站區塊之外定義的特殊區塊,以 &( 開頭並以 ) 結尾,中間是名稱。 &(app-proxy) { reverse_proxy app-01:8080 app-02:8080 app-03:8080 } 然後你可以在任何網站中重複使用此命名路由: example.com { invoke app-proxy } www.example.com { invoke app-proxy } 如果許多不同的網站需要相同的路由,或者需要多個不同的 matcher 條件來調用相同的路由,這對於減少記憶體使用特別有用。 閱讀 invoke 指令頁面 以了解更多資訊。 註釋 註釋以 # 開始,一直持續到行尾: # 註釋可以從一行開始 directive # 或者放在結尾 註釋的井號字符 # 不能出現在標記的中間(即它必須前面有一個空格或出現在一行的開頭)。這允許在 URI 或其他值中使用井號而不需要加引號。 環境變數 如果你的配置依賴於環境變數,你可以在 Caddyfile 中使用它們: {$ENV} 這種形式的環境變數在 Caddyfile 解析開始之前 被替換,因此它們可以展開為空值(即 "")、部分標記、完整標記,甚至是多個標記和行。 例如,環境變數 UPSTREAMS="app1:8080 app2:8080 app3:8080" 將展開為多個 標記: example.com { reverse_proxy {$UPSTREAMS} } 當找不到環境變數時,可以通過在變數名和預設值之間使用 : 作為分隔符來指定預設值: {$DOMAIN:localhost} { } 如果你想 延遲環境變數的替換 直到運行時,可以使用 標準 {env.*} placeholder。請注意,並非所有配置參數都支持這些 placeholder,因為模組開發人員需要添加一行代碼來執行替換。如果它似乎不起作用,請提交問題以請求支持。 例如,如果你安裝了 caddy-dns/cloudflare 外掛 並希望配置 DNS 挑戰,你可以將 CLOUDFLARE_API_TOKEN 環境變數傳遞給外掛,如下所示: { acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} } 如果你將 Caddy 作為 systemd 服務運行,請參見 這些說明 以設置服務覆蓋來定義你的環境變數。