很早之前就听说过caddy这个开源的web服务器,但一直也没尝试过。最近刚好使用caddy配置了一个站点,发现真香!
为什么要使用 Caddy
- 安全 Caddy 是一个默认使用https协议的web服务器
- 无依赖 Caddy 使用 Go 语言编写,编译好的二进制文件能够运行在任何支持Go语言的平台,不需要自己安装任何库。
- 使用简单 Caddy 的配置简单,不管你是新的web开发者,还是专业人士,都能够快速上手
- 可实现自动SSL证书申请和续期
一、Caddy简介
Caddy是使用Go语言编写的一款开源Web服务器和反向代理服务器,设计目标是提供易于使用且高效的性能。它不仅支持常见的HTTP/HTTPS协议,还可以作为反向代理服务器、负载均衡器、WebSocket支持等。它的灵活性和模块化的架构,使其能够根据不同需求扩展功能,特别适合用于容器化环境和微服务架构。
个人体验下来Caddy的有几个比较大的特点
第一点、默认启用HTTPS,Caddy集成了Let’s Encrypt,可以自动为你的网站申请、更新和管理SSL证书, 无需任何额外操作,免去繁琐的配置和证书管理流程。
第二点、配置简洁,与传统的Web服务器相比,Caddy的配置文件极为简洁,使用简易的配置文件(Caddyfile),极大降低了新手的学习成本。
第三点,除了传统的Caddyfile和JSON配置文件外,Caddy还提供了通过 REST API 动态管理和变更其配置的能力。这个API使得我们能够在运行时更改Caddy的配置,而无需重新启动服务器或手动编辑配置文件。
第四点,现代化,通过默认启用 HTTPS 和 REST API 动态变更配置也能看出来,除此之外caddy还支持Prometheus metrics、默认使用结构化的json作为access日志。
对比传统的web服务器Nginx对比更能看出caddy的一系列特点
特性 | Caddy | Nginx |
---|---|---|
配置方式 | Caddyfile, JSON, REST API | Nginx配置文件(nginx.conf) |
自动HTTPS支持 | 是,默认启用自动TLS证书管理 | 否,需手动配置SSL证书 |
适用范围 | 7层(应用层),反向代理和Web服务,内置负载均衡 | 支持4层(传输层)和7层(应用层)反向代理、负载均衡等 |
扩展性 | 插件化架构,支持扩展 | 模块化架构,支持静态编译的模块 |
性能 | 较高(适合轻量应用) | 非常高(适合高并发应用) |
配置简洁性 | Caddyfile格式简洁,易于上手 | 配置相对复杂,灵活但不够直观 |
系统资源占用 | 较低 | 较低,适合高并发处理 |
编写语言 | Go语言 | C语言 |
Access日志格式 | 结构化,默认JSON格式,支持自定义 | 非结构化,默认标准日志格式,支持自定义 |
二、Caddy的基本用法
Caddy的安装和配置非常简便,下面是一些常见的配置示例。
1. Caddy的安装
Caddy可以通过多种方式进行安装,除了传统的安装方法,还可以通过Docker Compose来进行快速部署。
方法一:二进制安装
安装方式除了可以使用发行版提供的仓库之外
因为caddy使用Go编写,编译完成后只有一个二进制文件,所以也可以直接在官网或者github release页面进行下载,下载完成后把caddy移动到PATH下即可
# 1、下载
wget https://github.com/caddyserver/caddy/releases/download/v2.9.1/caddy_2.9.1_linux_amd64.tar.gz
# 2、解压
tar -xf caddy_2.9.1_linux_amd64.tar.gz
# 3、移动文件到/usr/local/bin/
mv caddy /usr/local/bin/
# 4、查看版本
caddy version
直接启动Caddy
可以直接在命令行中手动启动 Caddy。运行以下命令:
caddy start
systemctl启动
官方也提供了systemd unit files,配置之后就可以使用systemd来启动了。
我们需要将以下内容写入 /etc/systemd/system/caddy.service 文件:
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile --force
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
也可以使用以下命令启动Caddy:
# 启动Caddy服务
sudo systemctl start caddy
# 设置Caddy开机自启
sudo systemctl enable caddy
这样,Caddy服务会在后台启动,并且会随系统开机自动启动。
如果想查看Caddy的运行状态,可以使用:
# 查看Caddy服务的状态
sudo systemctl status caddy
如果需要停止Caddy服务,可以执行:
# 停止Caddy服务
sudo systemctl stop caddy
方法二:使用Docker Compose安装
如果你希望通过Docker容器来运行Caddy,可以使用Docker Compose来进行安装和启动。首先,在项目目录下创建一个 docker-compose.yml
文件,内容如下:
version: "3.8"
services:
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
environment:
- ACME_AGREE=true
volumes:
caddy_data:
caddy_config:
在上述配置中,
./Caddyfile
是你的Caddy配置文件,Docker容器将其挂载到Caddy的配置目录中。- 此外,
caddy_data
和caddy_config
用于持久化存储Caddy的TLS证书和配置文件。
启动Caddy服务:
docker-compose up -d
通过此方式,你可以轻松地将Caddy部署到Docker容器中,并且无需关心手动管理TLS证书,Caddy会自动处理。
方法三:编译安装
Caddy是使用Golang语言开发,自行编译需要先安装Golang开发环境:
安装Golang
# 1、下载
# 下载地址:https://go.dev/dl/
wget https://go.dev/dl/go1.23.5.linux-amd64.tar.gz
# 2、解压
tar -xf go1.23.5.linux-amd64.tar.gz -C /usr/local/
# 3、添加环境变量
# 添加到环境变量,编辑/root/.bash_profile文件,内容如下:
GO_PATH=/usr/local/go/bin
PATH=$PATH:$HOME/bin:$GO_PATH
export PATH
# 使环境变量生效
source /root/.bash_profile
# 4、查看版本
go version
# 5、配置goproxy
go env -w GOPROXY=https://goproxy.cn,direct
编译Caddy
# 1、下载
git clone "https://github.com/caddyserver/caddy.git"
# 2、切换目录
cd caddy/cmd/caddy/
# 3、构建
go build
# 4、复制文件到/usr/local/bin/
cp caddy /usr/local/bin/
# 5、其他
caddy
# 6、启动
# 6.1、前台运行
caddy run
# 或者
# 6.2、后台运行
# caddy start --config caddy.json
# caddy run --config nginx.conf --adapter nginx
# caddy run --config caddy.file --adapter caddyfile
caddy start
方法四:官方脚本安装
caddy 官方给出了一个安装脚本,执行上面的命令就可以一键安装 caddy,等执行结束后,使用 which caddy
,可以看到 caddy 已经被安装到了 /usr/local/bin/caddy
:
curl -s https://getcaddy.com | bash
2. 配置方式
Caddy的配置可以通过两种方式来管理:
- 配置文件方式:通过将配置写入Caddyfile或者JSON文件中,Caddy会自动加载配置,官方也提供了一系列的adapter来支持其他格式的配置文件
- API方式:通过Caddy的API接口动态修改配置,适用于更复杂的环境和自动化场景。
在实际应用中,但大多数人会选择 JSON + API 或 Caddyfile + CLI 的组合方式,不会混合使用两者,避免出现配置不一致。
通过Caddyfile文件配置Caddy
Caddyfile是Caddy最常用的配置文件格式,以简洁明了著称。大多数用户和大部分文档推荐使用这种格式来配置Caddy。
Caddyfile是一种基于块结构的配置格式,语法非常简洁且易于理解。每个配置项通常以 站点名称(通常是域名)作为起始,然后是需要的配置项。
默认情况下Caddyfile文件为位于/etc/caddy/Caddyfile
, Caddyfile的基本结构如下:
https://study.disign.me {
//TLS节点用于自动申请和配置SSL证书
tls {
dns cloudflare 这里是操作DNS的Token值。
resolvers 1.1.1.1
}
//这里是反向代理后端服务
reverse_proxy 127.0.0.1:3000
log {
output file /var/log/caddy/access.log {
mode 644
}
format json
}
}
在这个示例中, https://study.disign.me
是配置的站点名称,后续的内容是针对该站点的配置项。
Caddyfile的配置项可以包括但不限于:
- 反向代理:将请求转发到后台服务。
- TLS/SSL配置:启用HTTPS并管理证书。
- 路径匹配和重定向:根据请求路径来定义不同的处理方式。
Caddyfile的每一行都代表一个配置项,它非常易于编写和阅读,且支持丰富的功能。
通过JSON文件配置Caddy
虽然Caddyfile格式更加简洁,但是在一些高级使用场景中,JSON格式的配置文件更加灵活和强大。特别是在需要动态配置或者通过API接口修改配置时,JSON格式是更常见的选择。一个Caddy JSON配置文件的例子如下:
{
"apps": {
"http": {
"servers": {
"example": {
"listen": [":80"],
"routes": [
{
"match": [
{
"host": ["example.com"]
}
],
"handle": [
{
"handler": "static_response",
"body": "Hello, world!"
}
]
}
]
}
}
}
}
}
尽管JSON格式更为复杂,但它支持更多的高级功能,如动态配置、分布式管理等。通常,开发者在需要与其他系统进行集成时会选择这种格式。
通过API配置Caddy
除了传统的Caddyfile和JSON配置文件外,Caddy还提供了通过 REST API 动态管理和变更其配置的能力
Caddy的REST API允许你通过HTTP请求来控制Caddy的配置、状态和TLS证书管理等,无需重新启动服务器或手动编辑配置文件
API默认情况下监听在Caddy的管理端口(默认为 localhost:2019
)。通过API,你可以对Caddy进行以下操作
1. 获取当前配置
你可以通过API请求来获取当前Caddy的配置。默认情况下,Caddy配置是以JSON格式返回的。
curl -X GET http://localhost:2019/config/
返回的JSON数据将展示当前Caddy的所有配置,类似于Caddyfile的配置内容。
2. 添加配置
如果你需要动态修改配置,可以通过 PUT
请求来添加Caddy的配置。
举个例子,假设当前caddy没有加载任何配置文件,通过动态加载配置,创建一个 server hello
,它监听2015端口,并且返回 "Hello, world!"
curl localhost:2019/load \
-H "Content-Type: application/json" \
-d @- << EOF
{
"apps": {
"http": {
"servers": {
"hello": {
"listen": [":2015"],
"routes": [
{
"handle": [{
"handler": "static_response",
"body": "Hello, world!"
}]
}
]
}
}
}
}
}
EOF
curl localhost:2015
Hello, world!
此时你想动态的修改 server hello
curl -X PATCH http://localhost:2019/config/apps/http/servers/hello/routes \
-H "Content-Type: application/json" \
-d '[
{
"handle": [
{
"handler": "static_response",
"body": "Hello from Caddy API!"
}
]
}
]'
curl localhost:2015
Hello from Caddy API!
这个请求会将一个修改静态响应,返回 Hello from Caddy API!
3. 删除站点配置
通过API,你也可以删除某个站点或相关配置。例如,删除一个指定的站点配置:
curl -X DELETE http://localhost:2019/config/apps/http/servers/hello
这将删除 server hello
的配置
三、 常见配置示例
为了简化配置过程和提升可读性,我们将在后续的示例中使用Caddyfile格式,以下是几个常见的配置示例。
直接回复
localhost:2017 { # 要server的站点名,不写端口则默认443(https)或者80(http)
respond "Hello, world!" # 直接响应内容
}
如果配置只有一行, {}
在caddyfile中是可以省略的。但我还是习惯用 {}
包裹
localhost:2017
respond "Hello, world!"
配置静态文件
localhost:2016 { # 要server的站点名,不写端口则默认443(https)或者80(http)
root * /var/www/mysite # 静态文件的根路径
file_server { # 静态文件处理
browse # 如果没有index文件,则展示目录浏览模式
hide .git # 隐藏 .git
precompressed zstd br gzip # 启用压缩
}
}
如果只有localhost:2016并且上面的如果file_server不需要配置其他选项的时候
localhost:2016
root * /var/www/mysite
file_server browse
配置反向代理
这个配置将所有访问 example.com
的请求反向代理到本地的 8080
端口。
example.com {
reverse_proxy localhost:8000
}
还可以针对不同的path进行反向代理
example.com {
reverse_proxy /bar localhost:8000 # example.com/bar的内容会被转发到localhost:8000/bar
reverse_proxy /foo localhost:8001 # example.com/foo的内容会被转发到localhost:8000/foo
}
还可以针对反向代理配置更复杂的策略,如改写请求与响应等
example.com {
reverse_proxy /bar localhost:8000 # example.com/bar的内容会被转发到localhost:8000/bar
reverse_proxy /foo { # 针对example.com/foo配置更复杂的策略
to localhost:8001 # 转发到localhost:8001
rewrite / # 改写path,example.com/foo会被转发成localhost:8001/
header_up X-Forwarded-For {remote} # 增加新的header:X-Forwarded-For,内容为client ip
}
}
配置负载均衡
example.com {
reverse_proxy / backend1.example.com backend2.example.com
}
此配置将请求负载均衡地分发到 backend1.example.com
和 backend2.example.com
。
负载均衡也类似,有很多参数可以设置
一个复杂的DEMO
# 要server的站点名,不写端口则默认443(https)或者80(http)
# 使用http://代表不启用https
http://localhost:8000 {
respond "Hello, world!"
}
http://localhost:8001 {
respond "{path}"
}
localhost:8002 {
# 记录所有路径的访问日志
log {
# 访问日志写入/path/to/access.log
output file /path/to/access.log {
# 设置日志文件的权限
mode 644
}
# 日志格式为json
format json
}
# 使用handle来匹配路径
# 它和下面等价
# reverse_proxy /lb/* localhost:8000 localhost:8001
handle /lb/* {
reverse_proxy localhost:8000 localhost:8001
}
handle /proxy/* {
reverse_proxy {
to localhost:8001 # 转发到localhost:8001
rewrite / # 改写path,/proxy/*会被转发到/*
header_up X-Forwarded-For {remote} # 增加新的header:X-Forwarded-For,内容为client ip
}
}
handle /static/* {
uri strip_prefix /static # 移除/static前缀
root * /var/www/mysite # 静态文件的根路径
file_server { # 静态文件处理
browse # 如果没有index文件,则展示目录浏览模式
hide .git # 隐藏 .git
precompressed zstd br gzip # 启用压缩
}
}
}
四. Caddy的重要持久化存储
在Caddy的配置中,有几个重要的持久化存储目录,它们用于存储TLS证书、配置文件和其他关键数据。理解这些存储路径的作用可以帮助你更好地管理和迁移Caddy的部署。
1. 自定义的配置文件
这个就不多说了,就是上文一直提到配置文件,你需要放置到合理的位置
对于容器内,默认配置文件位置在 /etc/caddy/Caddyfile
。因此可以挂载这个文件来提供自定义的配置文件
# ...
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
2. Data Directory(数据目录)
Caddy会自动为每个网站生成并管理SSL/TLS证书,这些证书存储在Caddy的数据目录中。
默认情况下,如果设置了 XDG_DATA_HOME
环境变量,那么位置就是 $XDG_DATA_HOME/caddy/
,它是一个目录
没设置的话则取决于系统
OS | Data directory path |
---|---|
Linux, BSD | $HOME/.local/share/caddy |
Windows | %AppData%\Caddy |
macOS | $HOME/Library/Application Support/Caddy |
Plan 9 | $HOME/lib/caddy |
Android | $HOME/caddy (or /sdcard/caddy ) |
docker | /data |
这个目录用于存储所有与Caddy运行相关的数据,例如:
- TLS证书和私钥:Caddy会自动申请和续订证书,并将这些证书文件保存在数据目录中。
- 证书缓存:为了提高性能,Caddy会缓存证书验证和其他相关数据。
- ACME(自动证书管理环境)缓存:Caddy使用ACME协议与Let’s Encrypt等证书颁发机构通信,该缓存存储了所有的ACME响应数据。
所以数据目录不能被视为缓存,其内容并非临时的,也不仅仅是为了性能。因此,在不了解其影响的情况下,不应清除数据目录的内容。
因此在容器中我们也需要挂载对应目录,不然重启之后数据就没了
# ...
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data # 数据目录
volumes:
caddy_data: # 这里使用了docker volume来存储
3. Configuration Directory(配置目录)
caddy会把 最后一次有效的配置 保存到该目录中,也就是说如果你通过API设置的配置也会被持久化到这里。
当 caddy run --resume
命令启动的时候,就可以继续使用之前的配置,这时候你的自定义配置文件是不生效的
如果设置了 XDG_CONFIG_HOME
环境变量, 位置在 $XDG_CONFIG_HOME/caddy
.
没设置的话则取决于系统
OS | Config directory path |
---|---|
Linux, BSD | $HOME/.config/caddy |
Windows | %AppData%\Caddy |
macOS | $HOME/Library/Application Support/Caddy |
Plan 9 | $HOME/lib/caddy |
docker | /config |
因此在容器中我们也需要挂载对应目录,不然重启之后数据就没了
# ...
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data # 数据目录
- caddy_config:/config # 配置目录
volumes:
caddy_data:
caddy_config: # 这里使用了docker volume来存储
五. Caddy 常用命令
如果仅仅是本地调试而不是需要长久运行,可以通过简单命令实现启动一个HTTP服务器:
直接启动
caddy run
指定文件启动
你也可以指定一个 Caddyfile:
caddy run --config /path/to/Caddyfile
启动静态文件服务器
如果你需要启动一个静态文件服务器,你可以使用以下命令:
caddy file-server --listen :2015 --root ~/mysite
启动文件浏览器
如果你需要启动一个文件浏览器,你可以使用以下命令:
caddy file-server --browse
反向代理
如果你需要启动一个反向代理,你可以使用以下命令:
caddy reverse-proxy --from :2080 --to :9000
格式化配置文件
如果你需要格式化你的 Caddyfile,你可以使用以下命令:
caddy fmt --overwrite
六、Caddy 安装插件
上面文章介绍了如何安装使用 Caddy,但是 Caddy 的功能有时候并不能满足业务需求,如果想要使用更多的功能,就需要自定义编译 Caddy。
xcaddy 是 Caddy 官方制作的用于自定义编译 Caddy 的工具,它可以帮助我们快速的编译出符合自己需求的 Caddy。
安装 xcaddy
以Debian为例,我们按照官方的安装方法,首先,安装一些必要的软件包:
apt update
apt upgrade -y
apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates
配置Golang环境参考上面教程,此处略过。
然后把 go 加入系统环境变量:
curl -sSL https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key | gpg --dearmor > /usr/share/keyrings/xcaddy.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/xcaddy.gpg] https://dl.cloudsmith.io/public/caddy/xcaddy/deb/debian any-version main" > /etc/apt/sources.list.d/xcaddy.list
然后更新系统后即可安装 xcaddy:
apt update
apt install xcaddy
重启打开 SSH 后检查一下 go 和 xcaddy 的版本:
root@debian ~ # go version
go version go1.19.5 linux/amd64
root@debian ~ # xcaddy version
v0.3.1 h1:XKmnGnGTeB53hLUgnGr/R4JbTNSxh8IBAFcJkrtycso=
自定义编译 Caddy
我们可以选择一些自己喜欢的模块,比如缓存模块和 Brotli 压缩模块:
xcaddy build \
--with github.com/caddy-dns/alidns \
--with github.com/caddy-dns/cloudflare \
--with github.com/caddy-dns/dnspod \
--with github.com/caddy-dns/godaddy \
--with github.com/caddy-dns/googleclouddns \
--with github.com/caddyserver/cache-handler \
--with github.com/ueffel/caddy-brotli
经过一段时间的编译以后,我们就可以在当前目录下看到一个名为 caddy 的二进制文件,这就是我们自定义编译的 Caddy 了。
总结
Caddy是一款易于使用、功能强大的现代Web服务器,适合快速部署,尤其是自动申请和续期Let’s Encrypt的HTTPS证书,真香!其与Nginx相比,最大的优势在于配置简便、内置HTTPS支持及开箱即用的功能,尤其适合中小型网站和开发环境。