Caddyfile 概念
本文档将帮助您详细了解 HTTP Caddyfile。
结构
Caddyfile 的结构可以用可视化方式描述
要点
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 处理程序指令会根据特定的默认 指令顺序 进行排序,除非在 route
块中,因此指令的出现顺序无关紧要,除非在 route
块中。
令牌和引号
Caddyfile 在解析之前会被词法分析为令牌。空格在 Caddyfile 中很重要,因为令牌由空格分隔。
通常,指令期望一定数量的参数;如果单个参数的值包含空格,则会被词法分析为两个单独的令牌
directive abc def
这可能会导致问题并返回错误或意外行为。
如果 abc def
应该是一个参数的值,则需要用引号引起来
directive "abc def"
如果需要在带引号的令牌中使用引号,则可以对引号进行转义
directive "\"abc def\""
为了避免转义引号,您也可以使用反引号 ` `
来括起令牌;例如
directive `{"foo": "bar"}`
在带引号的令牌内,所有其他字符都按字面意思处理,包括空格、制表符和换行符。因此可以实现多行令牌
directive "first line
second line"
Heredoc 也受支持:
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 匹配器 |
example.com:443 |
HTTPS,由于匹配 https_port 默认值 |
:443 |
HTTPS 捕获所有请求,由于匹配 https_port 默认值 |
:8080 |
非标准端口上的 HTTP,没有 Host 匹配器 |
localhost:8080 |
非标准端口上的 HTTPS,由于具有有效域名 |
https://example.com:443 |
HTTPS,但 https:// 和 :443 都是冗余的 |
127.0.0.1 |
HTTPS,带有本地信任的 IP 证书 |
http://127.0.0.1 |
HTTP,带有 IP 地址 Host 匹配器(拒绝 localhost ) |
从地址中,Caddy 可以潜在地推断出站点的方案、主机和端口。如果地址没有端口,Caddyfile 将选择与指定的方案匹配的端口,或者假定默认端口 443。
如果您指定了主机名,则只有带有匹配 Host
标头的请求才会被接受。换句话说,如果站点地址是 localhost
,则 Caddy 将不会匹配到 127.0.0.1
的请求。
可以使用通配符 (*
),但仅用于表示主机名的精确一个标签。例如,*.example.com
匹配 foo.example.com
,但不匹配 foo.bar.example.com
,而 *
匹配 localhost
,但不匹配 example.com
。有关实际示例,请参阅 通配符证书模式。
要捕获所有主机,请省略地址的主机部分,例如,只需 https://
即可。当使用 按需 TLS 时,当您事先不知道域名时,这非常有用。
如果多个站点共享相同的定义,您可以将它们全部列在一起,用空格和逗号分隔(至少需要一个空格)。以下三个示例是等效的
# Comma separated site addresses
localhost:8080, example.com, www.example.com {
...
}
或
# Space separated site addresses
localhost:8080 example.com www.example.com {
...
}
或
# Comma and new-line separated site addresses
localhost:8080,
example.com,
www.example.com {
...
}
地址必须是唯一的;您不能多次指定相同的地址。
占位符 不能 在地址中使用,但您可以在其中使用 Caddyfile 样式的 环境变量
{$DOMAIN:localhost} {
...
}
默认情况下,站点绑定到所有网络接口。如果您希望覆盖此行为,请使用 bind
指令 或 default_bind
全局选项 来执行此操作。
匹配器
HTTP 处理程序 指令 默认应用于所有请求(除非另有说明)。
请求匹配器 可用于根据给定条件对请求进行分类。使用匹配器,您可以准确指定某个指令适用于哪些请求。
对于支持匹配器的指令,指令后的第一个参数是匹配器令牌。以下是一些示例
root * /var/www # matcher token: *
root /index.html /var/www # matcher token: /index.html
root @post /var/www # matcher token: @post
匹配器令牌可以完全省略以匹配所有请求;例如,如果下一个参数看起来不像路径匹配器,则不需要给出 *
。
阅读请求匹配器页面 以了解更多信息。
占位符
占位符 是一种将动态值注入到静态配置中的简单方法。它们可以用作指令和子指令的参数。
占位符两侧用花括号 { }
括起来,并在内部包含标识符,例如:{foo.bar}
。可以转义左花括号 \{like.this}
以防止替换。占位符标识符通常使用点进行命名空间划分,以避免跨模块冲突。
哪些占位符可用取决于上下文。并非所有占位符都在配置的所有部分中可用。例如,HTTP 应用程序设置的占位符 仅在与处理 HTTP 请求相关的配置区域(即在 HTTP 处理程序 指令 和 匹配器 中,但不在 tls
配置 中)中可用。某些指令或匹配器也可能设置自己的占位符,这些占位符可以被随后的一切使用。一些占位符 是全局可用的。
您可以在 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} |
{path.*} |
{http.request.uri.path.*} |
{path} |
{http.request.uri.path} |
{port} |
{http.request.port} |
{query.*} |
{http.request.uri.query.*} |
{query} |
{http.request.uri.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} |
{vars.*} |
{http.vars.*} |
并非所有配置字段都支持占位符,但大多数您期望支持的地方都支持。对占位符的支持需要显式添加到这些字段中。插件作者可以阅读本文,了解如何在他们自己的模块中添加对占位符的支持。
代码片段
您可以通过给代码片段命名并用括号括起来来定义特殊的代码片段块
(logging) {
log {
output file /var/log/caddy.log
format json
}
}
然后您可以使用特殊的 import
指令在任何需要的地方重用它
example.com {
import logging
}
www.example.com {
import logging
}
import
指令也可以用于在其位置包含其他文件。如果参数与已定义的代码片段不匹配,则会尝试将其作为文件。它还支持 glob 来导入多个文件。作为一种特殊情况,它可以出现在 Caddyfile 中的任何位置(除了作为另一个指令的参数),包括站点块之外
{
email admin@example.com
}
import sites/*
您可以将参数传递给导入的配置(代码片段或文件),并像这样使用它们
(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) {
{block}
respond "OK"
}
a.example.com {
import snippet {
header +foo bar
}
}
b.example.com {
import snippet {
header +bar foo
}
}
阅读 import
指令页面 以了解更多信息。
命名路由
⚠️ 实验性
命名路由使用类似于 代码片段 的语法;它们是在站点块外部定义的特殊块,以 &(
为前缀,以 )
结尾,名称在两者之间。
&(app-proxy) {
reverse_proxy app-01:8080 app-02:8080 app-03:8080
}
然后您可以在任何站点中重用此命名路由
example.com {
invoke app-proxy
}
www.example.com {
invoke app-proxy
}
如果许多不同的站点都需要相同的路由,或者需要多个不同的匹配器条件来调用相同的路由,则这对于减少内存使用量特别有用。
阅读 invoke
指令页面 以了解更多信息。
注释
注释以 #
开头,并持续到行尾
# Comments can start a line
directive # or go at the end
注释的井号字符 #
不能出现在令牌的中间(即,它前面必须有一个空格或出现在行的开头)。这允许在 URI 或其他值中使用井号,而无需引号。
环境变量
如果您的配置依赖于环境变量,则可以在 Caddyfile 中使用它们
{$ENV}
这种形式的环境变量在 Caddyfile 解析开始之前 被替换,因此它们可以扩展为空值(即 ""
)、部分令牌、完整令牌,甚至多个令牌和行。
例如,环境变量 UPSTREAMS="app1:8080 app2:8080 app3:8080"
将扩展为多个 令牌
example.com {
reverse_proxy {$UPSTREAMS}
}
当找不到环境变量时,可以使用 :
作为变量名和默认值之间的分隔符来指定默认值
{$DOMAIN:localhost} {
}
如果您想延迟环境变量的替换直到运行时,您可以使用 标准 {env.*}
占位符。请注意,并非所有配置参数都支持这些占位符,因为模块开发人员需要添加一行代码来执行替换。如果它似乎不起作用,请提交问题以请求支持。
例如,如果您安装了 caddy-dns/cloudflare
插件 并且希望配置 DNS 挑战,您可以将您的
CLOUDFLARE_API_TOKEN
环境变量传递给插件,如下所示
{
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
如果您以 systemd 服务运行 Caddy,请参阅 这些说明,了解有关设置服务覆盖以定义环境变量的信息。