Caddy2 简介
Caddy 2 是一款用 Go 语言编写的、企业级开源 Web 服务器,其最突出的特性是默认开启自动 HTTPS。相比 Nginx、Apache 等传统 Web 服务器,Caddy2 在证书管理、配置简洁性和开发体验上有显著优势。
核心特性
| 特性 | 说明 |
|---|---|
| 自动 HTTPS | 默认开启,自动申请/续期 Let's Encrypt / ZeroSSL 证书,自动 HTTP→HTTPS 重定向 |
| Caddyfile 配置 | 人类可读的简洁配置格式,几行即可完成反向代理+HTTPS |
| JSON 配置 + Admin API | 完整的 JSON 结构化配置,支持通过 API 动态修改 |
| 反向代理 | 一等公民支持,含负载均衡、健康检查、Header 操作、多传输层 |
| 零停机重载 | caddy reload 不中断连接即可应用新配置 |
| 高性能 | Go 编写,生产级性能,支持 gzip/zstd 压缩 |
| 插件扩展 | Go Module 插件体系,可自定义指令、Handler、TLS Provider |
Caddyfile 配置语法
Caddyfile 是 Caddy2 原生的配置格式,简洁且表达力强。
静态文件服务
example.com {
root * /var/www/html
file_server
}反向代理
example.com {
reverse_proxy localhost:8080
}仅需这两行,Caddy 就会自动为 example.com 申请证书并反向代理到 8080 端口。
多路径路由
example.com {
handle /api/* {
reverse_proxy localhost:3000
}
handle /static/* {
root * /var/www
file_server
}
handle /* {
reverse_proxy localhost:8080
}
}带负载均衡的反向代理
example.com {
reverse_proxy localhost:3000 localhost:3001 localhost:3002 {
lb_policy round_robin
health_uri /health
health_interval 10s
}
}负载均衡策略:round_robin(轮询,默认)、least_conn(最少连接)、random(随机)、ip_hash(IP哈希)、uri_hash(URI哈希)
自动 HTTPS 详解
Caddy2 是唯一默认开启自动 HTTPS 的主流 Web 服务器。
- Caddy 启动时扫描配置中所有域名
- 对合格域名自动通过 ACME 协议向 CA(Let's Encrypt / ZeroSSL)申请证书
- 自动配置 TLS 并将 HTTP 请求重定向到 HTTPS
- 证书到期前自动续期(默认提前约 30 天)
按需 TLS(On-Demand TLS)
对于运行时才知道的域名(如 SaaS 多租户场景),可启用按需证书:
{
on_demand_tls {
ask http://localhost:5555/check
}
}
:443 {
tls {
on_demand
}
reverse_proxy localhost:8080
}内部 CA(开发/内网)
https://localhost {
tls internal
reverse_proxy localhost:8080
}使用 Caddy 自签名 CA 签发证书,适合本地开发。
Docker Compose 部署
基础 Docker 运行
docker run -d \
--name caddy \
-p 80:80 \
-p 443:443 \
-v caddy_data:/data \
-v caddy_config:/config \
-v $PWD/Caddyfile:/etc/caddy/Caddyfile:ro \
caddy:2信息
/data 目录存放 TLS 证书,必须持久化,否则容器重启会重新申请证书,可能触发速率限制!
Docker Compose 单服务部署
创建 docker-compose.yaml:
services:
caddy:
image: caddy:2
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- webnet
app:
image: myapp:latest
container_name: app
restart: unless-stopped
expose:
- "3000"
networks:
- webnet
volumes:
caddy_data:
caddy_config:
networks:
webnet:对应 Caddyfile:
example.com {
reverse_proxy app:3000
}提示
在 Docker 网络中,Caddy 直接用服务名 app 作为主机名即可访问。
Docker Compose 多服务反向代理
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- webnet
frontend:
image: my-frontend:latest
expose:
- "80"
networks:
- webnet
api:
image: my-api:latest
expose:
- "3000"
networks:
- webnet
blog:
image: my-blog:latest
expose:
- "8080"
networks:
- webnet
volumes:
caddy_data:
caddy_config:
networks:
webnet:Caddyfile:
example.com {
handle /api/* {
reverse_proxy api:3000
}
handle /blog/* {
reverse_proxy blog:8080
}
handle /* {
reverse_proxy frontend:80
}
}自定义 Caddy 镜像(含插件)
当需要 DNS 挑战插件(如 Cloudflare、Route53)时,需自定义构建:
FROM caddy:2-builder AS builder
RUN xcaddy build \
--with github.com/caddy-dns/cloudflare
FROM caddy:2
COPY --from=builder /usr/bin/caddy /usr/bin/caddy使用 xcaddy 工具编译含插件的 Caddy,适用于 DNS 挑战方式申请证书(无需开放 80 端口)。
反向代理深入
健康检查
主动健康检查:
reverse_proxy localhost:8080 {
health_uri /health
health_interval 10s
health_timeout 5s
health_status 200
}被动健康检查:
reverse_proxy localhost:8080 {
fail_duration 30s
max_fails 3
unhealthy_status 500 502 503
}Header 操作
reverse_proxy localhost:8080 {
# 添加/修改请求头
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
# 删除响应头
header_down -Server
# 设置响应头
header_down X-Frame-Options DENY
}传输层配置
reverse_proxy localhost:8080 {
transport http {
tls
keep_alive 30s
compression off
versions 1.1 2
}
}Admin API
Caddy2 内置 RESTful 管理 API,默认监听 localhost:2019。
# 查看当前配置
curl localhost:2019/config/
# 更新配置(零停机)
curl localhost:2019/load \
-X POST \
-H "Content-Type: application/json" \
-d @caddy.json
# 重载 Caddyfile
caddy reload
# 停止
curl localhost:2019/stop常用场景配置
WebSocket 代理
ws.example.com {
reverse_proxy localhost:4000
}Caddy2 自动支持 WebSocket 代理,无需额外配置。
SPA 应用 + API 代理
app.example.com {
root * /srv/spa/dist
file_server
@notStatic {
not path /api/*
not path *.js *.css *.png *.jpg *.svg *.ico *.woff2
}
rewrite @notStatic /
handle /api/* {
reverse_proxy api:3000
}
}内网服务 TLS 加密
https://internal.local {
tls internal
reverse_proxy localhost:8080
}与 Nginx 对比
| 对比项 | Caddy2 | Nginx |
|---|---|---|
| 自动 HTTPS | ✅ 默认开启 | ❌ 需 Certbot 手动配置 |
| 配置语法 | Caddyfile(简洁) | nginx.conf(繁琐) |
| 动态配置 | Admin API 原生支持 | 需 reload / OpenResty |
| 语言 | Go | C |
| 插件 | Go Module | C 模块(编译) |
| 负载均衡 | 内置 | 内置(更成熟) |
| 社区生态 | 中等 | 非常成熟 |
| 适合场景 | 中小规模、HTTPS优先、Docker 友好 | 大规模、高性能、复杂路由 |
常见问题
Q: 证书申请失败?
检查 80/443 端口是否开放,域名 DNS 是否正确指向服务器,是否有防火墙拦截。
Q: Docker 中证书持久化?
必须将 /data 目录挂载到 Docker Volume 或宿主机目录,否则容器重启后证书丢失。
Q: 非 80/443 端口能否自动 HTTPS?
不能。ACME HTTP-01 验证要求 80 端口可达;TLS-ALPN-01 要求 443 端口可达。内网环境用 tls internal。
评论区