caddy
Caddy
Caddy 是一个 Go 编写的 Web 服务器,类似于 Nginx,Caddy 提供了更加强大的功能,随着 v2 版本发布 Caddy 已经可以作为中小型站点 Web 服务器的另一个选择;相较于 Nginx 来说使用 Caddy 的优势如下:
- 自动的 HTTPS 证书申请(ACME HTTP/DNS 挑战)
- 自动证书续期以及 OCSP stapling 等
- 更高的安全性包括但不限于 TLS 配置以及内存安全等
- 友好且强大的配置文件支持
- 支持 API 动态调整配置(有木有人可以搞个 Dashboard?)
- 支持 HTTP3(QUIC)
- 支持动态后端,例如连接 Consul、作为 k8s ingress 等
- 后端多种负载策略以及健康检测等
- 本身 Go 编写,高度模块化的系统方便扩展(CoreDNS 基于 Caddy1 开发)
一、Caddyfile
1、简单使用
在当前目录下新建Caddyfile文件
localhost {
respond "Hello, world!"
}
localhost:2016 {
respond "Goodbye, world!"
}
服务管理
# 启动服务
caddy start
# 重启服务,重启服务有两种方式
caddy reload
curl localhost:2019/load \
-X POST \
-H "Content-Type: text/caddyfile" \
--data-binary @Caddyfile
# 停止服务
caddy stop
2、静态文件管理
显示文件列表
localhost
file_server browse
文件夹作为站点根目录:
localhost
root * /home/me/mysite
file_server
3、反向代理
localhost
reverse_proxy 127.0.0.1:9000
4、启用压缩算法
localhost
encode zstd gzip
file_server browse
5、多个站点
:8080 {
respond "I am 8080"
}
:8081 {
respond "I am 8081"
}
多端口
:8080, :8081 {
...
}
6、匹配器
对某一个api使用反向代理
localhost
file_server
reverse_proxy /api/* 127.0.0.1:9005
# 现在反向代理只会处理所有以/api/开始的请求。
使用环境变量
export SITE_ADDRESS=localhost:9055
{$SITE_ADDRESS}
file_server
root * /var/www # matcher token: *
root /index.html /var/www # matcher token: /index.html
root @post /var/www # matcher token: @post
占位符
简写 | 替换 |
---|---|
{dir} |
{http.request.uri.path.dir} |
{file} |
{http.request.uri.path.file} |
{header.*} |
{http.request.header.*} |
{host} |
{http.request.host} |
{labels.*} |
{http.request.host.labels.*} |
{hostport} |
{http.request.hostport} |
{port} |
{http.request.port} |
{method} |
{http.request.method} |
{path} |
{http.request.uri.path} |
{path.*} |
{http.request.uri.path.*} |
{query} |
{http.request.uri.query} |
{query.*} |
{http.request.uri.query.*} |
{re.*.*} |
{http.regexp.*.*} |
{remote} |
{http.request.remote} |
{remote_host} |
{http.request.remote.host} |
{remote_port} |
{http.request.remote.port} |
{scheme} |
{http.request.scheme} |
{uri} |
{http.request.uri} |
{tls_cipher} |
{http.request.tls.cipher_suite} |
{tls_version} |
{http.request.tls.version} |
{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_client_certificate_pem} |
{http.request.tls.client.certificate_pem} |
{tls_client_certificate_der_base64} |
{http.request.tls.client.certificate_der_base64} |
{upstream_hostport} |
{http.reverse_proxy.upstream.hostport} |
在Caddyfile中,紧跟在指令后面的匹配器标记可以限制该指令的范围。匹配器标记可以是以下形式之一:
\*
匹配所有请求(通配符;默认)。/path
以正斜杠开头以匹配请求路径。@name
指定一个命名匹配器。
匹配器标记通常是可选的。如果省略匹配器标记,则它与通配符匹配器(*
)相同。
命名匹配器
要匹配路径以外的任何内容,请定义一个命名匹配器并使用@name
引用它:
@postfoo {
method POST
path /foo/*
}
reverse_proxy @postfoo localhost:9000
@websockets {
header Connection *Upgrade*
header Upgrade websocket
}
reverse_proxy @websockets localhost:6001
file
file {
root <paths>
try_files <files...>
try_policy first_exist|smallest_size|largest_size|most_recent_modified
split_path <delims...>
}
通过文件进行匹配。
-
root
定义在其中查找文件的目录。默认是当前工作目录,或者root
变量 ({http.vars.root}
)对应的位置 (可以通过root
指令设置)。 -
try_files
检查其列表中与重试策略(try_policy)匹配的文件。如果try_policy
是first_exist
,那么列表中的最后一项可能是一个以=
(比如=404
)开头的数字,作为后备,将触发以这个数字作为错误码的回调; 该错误也可以使用handle_errors
捕获和处理错误。 -
try_policy
指定如何选择文件。默认为
first_exist
.
first_exist
检查文件是否存在。选择存在的第一个文件。smallest_size
选择大小最小的文件。largest_size
选择最大的文件。most_recent_modified
选择最近修改的文件。
-
split_path
将导致路径在每个要尝试的文件路径中找到的列表中的第一个分隔符处拆分。对于每个拆分值,拆分的左侧(包括分隔符本身)将是尝试的文件路径。例如,/remote.php/dav/
使用.php
作为分隔符,将尝试文件/remote.php
。每个分隔符必须出现在 URI 路径组件的末尾,才能用作拆分分隔符。这是一个小众设置,主要用于为 PHP 站点提供服务。
7、地址
有效地址:
localhost
example.com
:443
http://example.com
localhost:8080
127.0.0.1
[::1]:2015
example.com/foo/*
*.example.com
http://
注意:如果你的站点地址包含主机名或 IP 地址,则会启用自动HTTPS。然而,这种行为纯粹是隐含的,因此它永远不会覆盖任何显式配置。例如,如果站点的地址是http://example.com
,则不会激活自动HTTPS,因为该方案是明确的http://
。
如果找不到文件,则回退到发出404错误。
file {path}.html {path} =404
header
header <field> [<value>]
通过请求头字段进行匹配。
-
<field>
是要检查的 HTTP 标头字段的名称。
- 如果以
!
为前缀,则该字段必须不存在才能匹配 (省略value
参数).
- 如果以
-
<value>
是字段必须匹配的值。
- 如果前缀是
*
,则执行快速后缀匹配。 - 如果后缀为
*
,则执行快速前缀匹配。 - 如果用
*
括起来,它将执行快速子字符串匹配。 - 否则,它是快速精确匹配。
- 如果前缀是
统一集合的不同header字段是“和”的关系。每个字段的多个值之间是“或”的关系。
示例:
匹配请求Connection
标头字段包含Upgrade
的请求:
header Connection *Upgrade*
匹配Foo
标头字段包含bar
或者baz
的请求:
@foo {
header Foo bar
header Foo baz
}
匹配根本没有Foo
标头字段的请求:
@not_foo {
header !Foo
}
8、片段
可以定义称为片段的特殊块,方法是给它们一个用括号括起来的名称:
(redirect) {
@http {
protocol http
}
redir @http https://{host}{uri}
}
然后你可以在任何你需要的地方重复使用它:
import redirect
例如:
(snippet) {
respond "Yahaha! You found {args.0}!"
}
a.example.com {
import snippet "Example A"
}
b.example.com {
import snippet "Example B"
}
9、全局参数
{
# General Options
debug
http_port <port>
https_port <port>
order <dir1> first|last|[before|after <dir2>]
storage <module_name> {
<options...>
}
storage_clean_interval <duration>
admin off|<addr> {
origins <origins...>
enforce_origin
}
log [name] {
output <writer_module> ...
format <encoder_module> ...
level <level>
include <namespaces...>
exclude <namespaces...>
}
grace_period <duration>
# TLS Options
auto_https off|disable_redirects|ignore_loaded_certs
email <yours>
default_sni <name>
local_certs
skip_install_trust
acme_ca <directory_url>
acme_ca_root <pem_file>
acme_eab <key_id> <mac_key>
acme_dns <provider> ...
on_demand_tls {
ask <endpoint>
interval <duration>
burst <n>
}
key_type ed25519|p256|p384|rsa2048|rsa4096
cert_issuer <name> ...
ocsp_stapling off
preferred_chains [smallest] {
root_common_name <common_names...>
any_common_name <common_names...>
}
# Server Options
servers [<listener_address>] {
listener_wrappers {
<listener_wrappers...>
}
timeouts {
read_body <duration>
read_header <duration>
write <duration>
idle <duration>
}
max_header_size <size>
protocol {
allow_h2c
experimental_http3
strict_sni_host
}
}
}
二、使用示例
1、静态文件服务器
example.com {
root * /var/www
file_server
}
像往常一样,第一行是站点地址。该root
指令指定站点根目录的路径(*
匹配所有请求的方法,以便与路径匹配器消除歧义);如果站点不是当前工作目录,则更改站点的路径。最后,我们启用静态文件服务器。
2、反向代理服务器
代理所有请求
example.com {
reverse_proxy localhost:5000
}
只代理以/api/
开头的请求,并为其他所有内容提供静态文件:
example.com {}
root * /var/www
reverse_proxy /api/* localhost:5000
file_server
}
3、php
在运行PHP FastCGI服务的情况下,类似这样的内容适用于大多数现代PHP应用程序
example.com
root * /var/www
php_fastcgi /blog/* localhost:9000
file_server
请对应地调整站点根目录和路径匹配器;此示例假定PHP仅位于/blog/
子目录中——所有其他请求将作为静态文件提供。
该php_fastcgi
指令实际上只是几个配置的快捷方式。
4、重定向到www.
子域名
example.com {
redir https://www.example.com{uri}
}
www.example.com {
}
www.example.com {
redir https://example.com{uri}
}
example.com {
}
5、通配符证书
*.example.com {
tls {
dns <provider_name> [<params...>]
}
@foo host foo.example.com
handle @foo {
respond "Foo!"
}
@bar host bar.example.com
handle @bar {
respond "Bar!"
}
# Fallback for otherwise unhandled domains
handle {
abort
}
}
6、web
example.com { # 网站的域名信息
tls example.com.pem example.com.key # 证书和密钥的 PEM 格式的文件路径
encode zstd gzip # 启用压缩
root * ./ # 域名映射根路径
file_server # 启动文件服务
}
example2.com { # 网站的域名信息
tls example2.com.pem example2.com.key # 证书和密钥的 PEM 格式的文件路径
reverse_proxy localhost:9000 # 反向代理
}
三、部署
docker-compose.yaml
version: "3.7"
services:
caddy:
image: caddy:<version>
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- $PWD/Caddyfile:/etc/caddy/Caddyfile
- $PWD/site:/srv
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
caddyfile 示例
{
http_port 9080
# 设置http类型
servers {
protocols h1 h2
}
# 关闭admin api
admin off
# auto_https off|disable_redirects|ignore_loaded_certs
}
(TLSC) {
tls /etc/nginx/cert_files/local.com.crt /etc/nginx/cert_files/local.com.key
}
(LOGC) {
log {
format transform `{request>remote_ip} - {user_id} [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size} "{request>headers>Referer>[0]}" "{request>headers>User-Agent>[0]}"` {
time_format "iso8601"
}
# {args.0} 声明引用传入的第一个参数
output file {args.0} {
roll_size 100mb
roll_keep 7
roll_local_time
roll_keep_for 30d
}
}
}
https://terraform-registry.local.com:9090 {
encode zstd gzip
root * /data/terraform-registry
try_files {path} /index.html
file_server browse
import TLSC
import LOGC /var/log/nginx/tf-registry.log
}
(PROXY_HEADER) {
header_up Host {host}
header_up REMOTE-HOST {remote}
header_up X-Real-IP {remote}
header_up X-Forwarded-For {remote}
}
git.local.com:9090, local.com:9090 {
encode zstd gzip
import TLSC
import LOGC /var/log/nginx/local.com.log
reverse_proxy http://localhost4:5000 {
import PROXY_HEADER
}
}