Caddy 入门:自动 HTTPS 的 Web 服务器
2025-09-14
Caddy 是一个用 Go 写的 Web 服务器,最大的特点是配置简单、自动申请和续期 HTTPS 证书。我自己的博客和几个小服务都放在一台小 VPS 上,早先用 Nginx 加 certbot,证书续期、定时任务、重载配置各是一摊事;换成 Caddy 之后,一份十几行的配置文件就把这些都包了。这篇文章记录我的使用过程,从安装到静态站点、反向代理,再到常见的坑,环境是 Debian / Ubuntu。
Caddy 是什么,适合谁
Caddy 是单个可执行文件,默认只要你的域名解析正确、80 和 443 端口可达,它就会自动向证书机构申请证书、自动续期,并默认把 HTTP 跳转到 HTTPS。对个人站点来说,这省掉了最容易出错的一环。如果你对 HTTPS 的原理还不熟,可以先看《HTTPS 是怎么工作的》;域名解析的部分见《DNS 入门》。
它不适合的场景也要说清楚:如果你需要大量第三方模块、非常细的调优,或者已有成熟的 Nginx 配置体系,没必要为了尝鲜而迁移。
安装(Debian / Ubuntu)
推荐使用官方提供的 apt 软件源,这样后续可以通过 apt 升级。以下步骤请对照 Caddy 官方文档确认最新写法,因为密钥和源地址偶尔会调整。
sudo apt update sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl gpg 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 -y caddy
安装完成后,Caddy 会以 systemd 服务运行,默认配置文件是 /etc/caddy/Caddyfile。用下面的命令确认状态:
systemctl status caddy caddy version
开始之前的准备
- 域名的 A 记录(有 IPv6 的话再加 AAAA 记录)指向服务器公网 IP,例如 203.0.113.10。
- 服务器防火墙和云厂商安全组放行 80 与 443,详见《Linux 防火墙入门》。80 端口也要开,因为证书验证和 HTTP 跳转都会用到。
- 确认没有别的程序占用 80/443,可以用
sudo ss -ltnp查看。如果之前装过 Nginx 或 Apache,要先停掉。 - 如果服务器刚买来,请先按《VPS 初始化》和《SSH 安全》做好基本加固。
托管一个静态站点
www.example.com {
root * /home/user/blog
encode gzip
file_server
}
这里 root 指定网站根目录,encode gzip 开启压缩,file_server 启用静态文件服务。注意 Caddy 进程的运行用户(Debian 包里是 caddy)需要对目录有读取权限,而 /home/user 默认可能是 750 权限,会导致 403。我的做法是把站点放到 /var/www/blog 这样的位置:
sudo mkdir -p /var/www/blog
sudo cp -r ~/blog/. /var/www/blog/
sudo chown -R root:root /var/www/blog
sudo find /var/www/blog -type d -exec chmod 755 {} +
sudo find /var/www/blog -type f -exec chmod 644 {} +
然后把 Caddyfile 里的 root 改为 /var/www/blog。如果你想让 example.com 跳转到 www.example.com,可以再加一段:
example.com {
redir https://www.example.com{uri} permanent
}
反向代理
app.example.com {
reverse_proxy 127.0.0.1:8080
}
后端程序只监听本机端口,由 Caddy 对外提供 HTTPS,是常见的做法。如果后端是用 Docker 跑的,端口映射也要绑定到 127.0.0.1,见《Docker 入门》。Caddy 会自动带上 X-Forwarded-For 等请求头,多数应用不需要额外设置。
在同一个站点下混用静态文件和后端
有时我想让同一个域名下 /api/ 走后端,其他走静态文件,可以用 handle:
example.com {
encode gzip
handle /api/* {
reverse_proxy 127.0.0.1:8080
}
handle {
root * /var/www/blog
file_server
}
}
同一层级的多个 handle 是互斥的:匹配到第一个就不再往下走,不带匹配器的 handle 相当于兜底。如果想在转发前去掉路径前缀,可以改用 handle_path,它会自动剥离匹配到的前缀。
给管理页面加上基本认证
某些内部页面不想公开,但又懒得做完整的登录系统,可以用 basicauth。密码不能明文写在配置里,要先生成哈希:
caddy hash-password
命令会交互式提示你输入密码,输出一串以 $2a$ 开头的哈希。把哈希填到配置里(下面是占位示例,不是真实值):
admin.example.com {
basicauth {
alice $2a$14$REPLACE_WITH_YOUR_OWN_HASH
}
reverse_proxy 127.0.0.1:9000
}
基本认证只在 HTTPS 下才算可接受,Caddy 默认就是 HTTPS,所以问题不大;但它不能防暴力破解,密码要足够长。重要的后台建议再叠加 IP 限制或 VPN。
TLS 相关的几个选项
大多数情况下你不需要写 tls 指令。需要时常见的用法有两种。一是指定联系邮箱,方便证书机构发送到期或问题通知:
{
email admin@example.com
}
二是使用自己已有的证书文件:
example.com {
tls /etc/ssl/example.com/fullchain.pem /etc/ssl/example.com/privkey.pem
file_server
}
私钥文件权限要收紧,只让 caddy 用户能读。调试证书申请时,可以在全局选项里临时使用测试环境,避免反复失败触及正式环境的频率限制,具体的指令请查官方文档,不要凭记忆乱写。
常用命令
caddy validate --config /etc/caddy/Caddyfile sudo systemctl reload caddy sudo caddy fmt --overwrite /etc/caddy/Caddyfile journalctl -u caddy --no-pager -n 50
改配置前先备份,改完先 validate 再 reload,出错时能快速回退。caddy fmt 可以统一缩进,Caddyfile 对缩进不敏感,但格式统一更容易看出括号是否配对。配置文件也应该纳入备份,见《服务器备份与基础监控》。
常见问题排查
证书一直申请不下来
先看日志:journalctl -u caddy -n 100。最常见的原因有几个:域名没有解析到本机;80/443 被防火墙或云安全组挡住;有别的程序占用了端口;域名走了 CDN 的代理模式,验证请求到不了源站。逐项排除即可。
访问返回 403 或 404
403 通常是目录权限问题,上面提到过;404 多半是 root 路径写错,或者文件名大小写不一致。Linux 文件名区分大小写。
502 Bad Gateway
说明 Caddy 连不上后端。确认后端是否真的在监听:sudo ss -ltnp | grep 8080,以及配置里的地址和端口是否一致。容器里的服务要确认端口映射正确。
reload 没有生效
先 validate;如果语法没问题,再检查是否改的是正在使用的那个文件。用 systemd 启动时读取的是 /etc/caddy/Caddyfile。
和 Nginx 比
Nginx 生态更大、可调参数更多;Caddy 上手更快、自动证书省心。个人站点用 Caddy 完全够了。两者并不冲突,学会一个再看另一个,会更容易理解 Web 服务器在做什么。
小结
- 域名解析、80/443 放行是自动 HTTPS 的前提。
- 静态站点用
file_server,后端服务用reverse_proxy,后端只监听 127.0.0.1。 - 密码一律用
caddy hash-password生成哈希,不要写明文。 - 改完配置先 validate 再 reload,并备份配置文件。
- 出问题先看
journalctl -u caddy。