文档
一个 项目

Caddyfile 概念

本文档将帮助您详细了解 HTTP Caddyfile。

  1. 结构
  2. 全局选项
  3. 地址
  4. 匹配器
  5. 占位符
  6. 代码段
  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 的一个子指令(它设置了在后端之间使用的负载均衡策略)。

除非另有说明,否则指令不能在其他指令块中使用。 例如,basicauth 不能在 file_server 中使用,因为文件服务器不知道如何进行身份验证;但您可以在 routehandlehandle_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_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 时,这很有用,因为您事先不知道域名。

如果多个站点共享相同的定义,您可以将它们全部列在一起,使用空格或逗号。以下三个示例是等效的

# 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

如果下一个参数不像路径匹配器,则可以完全省略匹配器标记;例如,如果下一个参数不像路径匹配器,则不需要给出 *

阅读请求匹配器页面 以了解更多信息。

占位符

您可以在 Caddyfile 中使用任何 Caddy 占位符,但为了方便起见,您也可以使用一些等效的简写占位符

简写 替换
{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.*}
{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 指令也可以用来在它的位置包含其他文件。如果参数不匹配定义的代码段,它将被尝试作为文件。它还支持使用通配符导入多个文件。作为特殊情况,它可以出现在 Caddyfile 中的任何位置(除了作为另一个指令的参数),包括站点块之外

{
	email [email protected]
}

import sites/*

您可以将参数传递给导入的配置(代码段或文件),并像这样使用它们

(snippet) {
	respond "Yahaha! You found {args[0]}!"
}

a.example.com {
	import snippet "Example A"
}

b.example.com {
	import snippet "Example B"
}

阅读 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}
}

如果您正在将 Caddy 作为 systemd 服务运行,请参阅 这些说明 以设置服务覆盖以定义您的环境变量。