Caddy泛域名配置与自动化证书管理实践
1. Caddy 泛域名配置的核心思路
第一次在 Caddy 里配置泛域名时,我被它简洁的语法震惊了。相比 Nginx 复杂的正则表达式匹配,Caddy 只需要一个简单的*.example.com就能捕获所有子域名请求。这种设计哲学贯穿 Caddy 的整个配置体系 - 用最少的配置做最多的事。
泛域名配置的核心在于两个部分:通配符证书的获取和请求路由的分发。Caddy 通过 ACME 协议自动从 Let's Encrypt 获取泛域名证书,整个过程完全自动化。而在路由分发方面,handle块就像交通警察,根据不同的子域名将请求引导到对应的后端服务。
重要提示:使用泛域名证书前,请确保你的 DNS 解析已经将
*.example.com解析到服务器 IP,否则证书申请会失败。
2. 基础环境准备
2.1 Caddy 安装与基本配置
我推荐使用官方提供的安装脚本,这是最可靠的方式:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update sudo apt install caddy安装完成后,检查版本确保是最新的:
caddy version2.2 DNS 解析设置
在域名服务商处,你需要添加两条记录:
- 主域名 A 记录:
example.com -> 服务器IP - 泛域名 A 记录:
*.example.com -> 服务器IP
我常用 Cloudflare 来管理 DNS,它的传播速度很快。添加记录后,可以用 dig 命令测试是否生效:
dig +short test.example.com dig +short anything.example.com3. 泛域名证书自动获取
3.1 Caddyfile 基础配置
创建一个最简单的 Caddyfile 来测试证书获取:
*.example.com { tls { dns cloudflare {env.CLOUDFLARE_API_TOKEN} } }这里有几个关键点:
*.example.com表示匹配所有子域名tls块配置证书获取方式dns挑战使用 Cloudflare API 进行验证
注意:将 {env.CLOUDFLARE_API_TOKEN} 替换为你的实际 API Token,并确保该 Token 有足够的 DNS 编辑权限。
3.2 证书申请实战
启动 Caddy 服务:
export CLOUDFLARE_API_TOKEN="your_token_here" caddy start --config ./Caddyfile观察日志确认证书获取情况:
journalctl -u caddy -f你应该会看到类似这样的日志:
obtaining certificate for *.example.com waiting on DNS propagation for *.example.com successfully completed challenge for *.example.com4. 请求路由与 handle 块详解
4.1 基础路由结构
一个完整的泛域名路由配置通常长这样:
*.example.com { # 全局中间件和设置 encode gzip @api host api.example.com handle @api { reverse_proxy localhost:3000 } @app host app.example.com handle @app { root * /var/www/app file_server } handle { reverse_proxy localhost:8080 } }这个配置实现了:
api.example.com-> 3000 端口的 API 服务app.example.com-> 静态文件服务- 其他所有子域名 -> 8080 端口的默认服务
4.2 handle 块的高级用法
handle 块可以嵌套使用,实现更复杂的路由逻辑:
*.example.com { @blog { host blog.example.com path /posts/* } handle @blog { reverse_proxy localhost:2368 } @blog-admin { host blog.example.com path /admin/* } handle @blog-admin { reverse_proxy localhost:2369 { header_up Authorization {http.request.header.Authorization} } } }这个配置将:
blog.example.com/posts/*路由到 Ghost 的前端blog.example.com/admin/*路由到 Ghost 的后台- 并且为后台路由添加了认证头传递
5. 生产环境最佳实践
5.1 性能优化配置
经过多次压力测试,我总结出这些优化参数:
*.example.com { # 连接优化 reverse_proxy localhost:3000 { transport http { dial_timeout 10s keepalive 30s } lb_policy least_conn } # 缓存策略 header Cache-Control "public, max-age=3600" # 压缩设置 encode zstd gzip }5.2 安全加固措施
安全配置不容忽视:
*.example.com { # 安全头 header { X-XSS-Protection "1; mode=block" X-Frame-Options "SAMEORIGIN" Content-Security-Policy "default-src 'self'" } # 请求限制 @brute path /login handle @brute { rate_limit 10r/1m 100r/1h { key {http.request.remote.host} } reverse_proxy localhost:3000 } }6. 常见问题排查
6.1 证书申请失败
问题现象:日志显示 "DNS challenge failed"
排查步骤:
- 检查 DNS 解析是否生效
- 确认 API Token 权限足够
- 测试 DNS 传播:
dig +trace _acme-challenge.example.com TXT
6.2 路由不生效
问题现象:请求没有被正确路由到后端
排查方法:
- 检查 Caddy 调试日志:
caddy validate --config ./Caddyfile --adapter caddyfile - 确认 handle 块的匹配条件是否正确
- 测试路由匹配:
curl -H "Host: test.example.com" http://localhost
7. 进阶技巧与扩展
7.1 动态子域名处理
对于需要动态创建子域名的场景,可以使用 Caddy 的handle_path:
*.example.com { handle_path /subdomain/* { uri strip_prefix /subdomain rewrite * /{path} @dynamic host {http.request.host} reverse_proxy http://{http.request.host}.internal:8080 } }这个配置允许通过/subdomain/xxx访问xxx.example.com的内容。
7.2 多租户支持
使用 Caddy 的模板功能实现多租户:
*.example.com { @tenant host ~^(?P<tenant>[a-z0-9-]+)\.example\.com$ handle @tenant { reverse_proxy http://{http.request.host}:8080 { @notFound status 404 handle_response @notFound { rewrite * /{tenant}/404.html file_server } } } }这个配置会自动将tenant1.example.com路由到对应的租户服务。