文档
一个 项目

Caddyfile 概念

本文件将帮助你详细了解 HTTP Caddyfile。

  1. 结构
  2. 全局选项
  3. 地址
  4. 匹配器
  5. 占位符
  6. 片段(Snippets)
  7. 命名路由
  8. 注释
  9. 环境变量

结构

Caddyfile 的结构可以用视觉方式描述:

Caddyfile structure

要点:

  • 可选的 全局选项块 可以是文件中的第一个内容。

  • 之后可选地出现 片段命名路由

  • 否则,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_serverreverse_proxy 是指令。指令是在站点块中每行的第一个单词。

在第二个示例中,localhost:9000 是一个参数,因为它出现在指令之后的同一行。

有时指令可以打开它们自己的块。子指令出现在指令块中每行开头:

localhost {
	reverse_proxy localhost:9000 localhost:9001 {
		lb_policy first
	}
}

这里,lb_policyreverse_proxy 的一个子指令(它设置后端之间使用的负载均衡策略)。

除非另有文档说明,指令不能被用在其他指令块内。 例如,basic_auth 不能用在 file_server 内,因为文件服务器本身不具备进行认证的能力;但你可以在 routehandlehandle_path 块中使用指令,因为它们专门用于将指令组合在一起。

注意,当 HTTP Caddyfile 被适配时,HTTP 处理器指令会按照特定的默认 指令顺序 进行排序(除非在 route 块中),因此指令出现的顺序通常并不重要,route 块除外。

标记和引号

Caddyfile 在解析前会先词法分析为标记(tokens)。空白在 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 匹配器
example.com:443 由于匹配默认的 https_port 而使用 HTTPS
:443 由于匹配默认的 https_port 而作为 HTTPS 通配
: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(On-Demand TLS) 且事先不知道域名时,这很有用。

如果多个站点共享相同的定义,你可以把它们列在一起,用空格或逗号分隔(至少需要一个空格)。下面三个示例等价:

# 逗号分隔的站点地址
localhost:8080, example.com, www.example.com {
	...
}

# 空格分隔的站点地址
localhost:8080 example.com www.example.com {
	...
}

# 用逗号和换行分隔的站点地址
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.*}

并非所有配置字段都支持占位符,但大多数预期的字段都支持。对占位符的支持需要被显式添加到那些字段中。插件作者可以阅读这篇文章 以了解如何在他们自己的模块中添加对占位符的支持。

片段(Snippets)

你可以通过用括号包围名称来定义称为片段的特殊块:

(logging) {
	log {
		output file /var/log/caddy.log
		format json
	}
}

然后你可以在任何需要的地方重用它,使用特殊的 import 指令:

example.com {
	import logging
}

www.example.com {
	import logging
}

import 指令也可以用于在其位置包含其他文件。如果参数不匹配已定义的片段,则会尝试将其视为文件。它还支持通配符以导入多个文件。作为特例,它可以出现在 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 指令页面 以了解更多。

注释

注释以 # 开始,并一直延续到行尾:

# 注释可以在行首
directive  # 或者在行尾

注释用的井号字符 # 不能出现在标记的中间(即它前面必须是空格或出现在行首)。这允许在 URI 或其他值中使用井号而不需要加引号。

环境变量

如果你的配置依赖环境变量,你可以在 Caddyfile 中使用它们:

{$ENV}

这种形式的环境变量会在 Caddyfile 解析开始之前 被替换,因此它们可以扩展为空值(即 "")、部分标记、完整标记,甚至多个标记和多行。

例如,环境变量 UPSTREAMS="app1:8080 app2:8080 app3:8080" 会扩展为多个标记

example.com {
	reverse_proxy {$UPSTREAMS}
}

可以为在未找到环境变量时指定默认值,使用 : 作为变量名和默认值之间的分隔符:

{$DOMAIN:localhost} {

}

如果你想要将环境变量的替换推迟到运行时,可以使用标准的 {env.*} 占位符。请注意,并非所有配置参数都支持这些占位符,因为模块开发者需要添加一行代码来执行替换。如果它似乎不起作用,请提交 issue 请求对此的支持。

例如,如果你安装了 caddy-dns/cloudflare 插件 并希望配置 DNS 挑战,你可以像这样将 CLOUDFLARE_API_TOKEN 环境变量传递给该插件:

{
	acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}

如果你以 systemd 服务方式运行 Caddy,请参阅这些说明 这些说明 以设置服务覆盖来定义你的环境变量。