如何安装带自动 HTTPS 的 Caddy Web 服务器
从官方仓库在 Ubuntu 上安装 Caddy,为静态站点和反向代理编写 Caddyfile,理解自动证书、systemd、日志,以及从 Nginx 迁移时的常见错误。

Caddy 是默认获取并续期 TLS 证书的 Web 服务器。对于托管一两个域名的小型 VPS,这去掉了整整一类 Certbot 定时器和 Nginx 片段文件。配置语言(Caddyfile)很短。反向代理、gzip 和 HTTP/2 是普通功能,不是周末加班的额外模块。
对某些团队 Nginx 仍是正确选择:你已经有经过实战的配置,或需要非常特定的模块。本指南面向另一种常见情况——你想要在全新 Hiddence VPS 上能用的 HTTPS,而不必记住 include 片段。我们将从官方 apt 仓库安装 Caddy,解释它如何与 Let's Encrypt 对话,提供静态站点,代理本地应用,托管多个域名,查看日志,并处理与 Apache 或旧 Nginx 抢端口 80 的常见情况。
什么时候 Caddy 很合适
自动 HTTPS 是标题,但日常收益是更少的活动部件。Caddy 监听 80 和 443,把 HTTP 重定向到 HTTPS,并把证书存在数据目录。你仍然需要指向 VPS 的域名。你仍然需要不破坏 ACME(防火墙 80/tcp,没有别的进程偷走 :80)。通配符证书需要 DNS 提供商模块——那比单主机证书更长的一条路。
- 自动 HTTP→HTTPS 和证书续期
- 可读的 Caddyfile,而不是长长的 server 块
- 胜任 Node、Python、额外配置的 PHP-FPM,或 Docker 后端的反向代理
- HTTP/2 和现代 TLS 默认,不必 Spreadsheet 密码套件
- 官方软件包提供的 systemd 单元
要求
在 Caddy 能证明 ACME HTTP-01 之前,域名必须解析到这台 VPS。如果 DNS 仍在传播,Caddy 会签发失败并重试;看起来像「Caddy 坏了」,其实只是 DNS。如果 Apache 或 Nginx 占用 80/443,先停掉它们。
- Ubuntu 22.04 或 24.04
- 指向 VPS IP 的域名 A 记录(如果用 IPv6 还有 AAAA)
- 端口 80 和 443 空闲并已在防火墙中允许
- root 或 sudo
步骤 1:从官方仓库安装 Caddy
如果你想要当前的 TLS 和 ACME 行为,不要从默认 Ubuntu universe 随便装一个旧 Caddy。Caddy 项目文档里有 apt 源。安装后,caddy 用户存在,服务已启用。
ssh root@YOUR_VPS_IP
apt update && apt -y install debian-keyring debian-archive-keyring apt-transport-https curl gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update
apt -y install caddy
caddy version
systemctl status caddy --no-pager步骤 2:第一份 Caddyfile——静态站点
默认 Caddyfile 是 /etc/caddy/Caddyfile。用你的域名替换它。如果主机名不是 localhost,配置一加载 Caddy 就会尝试拿证书。把站点文件放在 caddy 用户可读的目录(常见是 www-data 风格权限,但软件包使用用户 caddy)。
mkdir -p /var/www/example
echo '<h1>It works</h1>' > /var/www/example/index.html
chown -R caddy:caddy /var/www/example
cat >/etc/caddy/Caddyfile <<'EOF'
example.com {
root * /var/www/example
file_server
encode gzip
}
EOF
caddy validate --config /etc/caddy/Caddyfile
systemctl reload caddy
# Watch issuance:
journalctl -u caddy -f步骤 3:反向代理本地应用
如果 Gunicorn、Node 或 Docker 监听 127.0.0.1:8000,Caddy 应该是唯一的公共进程。reverse_proxy 指令会以对大多数应用合理的方式转发 Host 和 X-Forwarded-* 头。如果应用生成绝对 HTTP URL,在应用里设置受信任代理 / HTTPS 标志(Django 的 SECURE_PROXY_SSL_HEADER、Express 的 trust proxy 等)。
app.example.com {
encode gzip
reverse_proxy 127.0.0.1:8000
}
# Several hosts in one file are normal:
# blog.example.com {
# root * /var/www/blog
# file_server
# }步骤 4:防火墙和 systemd
允许 80 和 443。Caddy 的单元是 caddy.service;如果进程健康,编辑 Caddyfile 后 reload 就够。如果为 DNS 插件更改环境变量,完整重启更清楚。证书默认在 /var/lib/caddy/.local/share/caddy/——如果你在意重装时的速率限制,把那条路径纳入备份。
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
systemctl enable --now caddy
systemctl reload caddy
# Backup cert storage (path may vary slightly by version):
ls -la /var/lib/caddy/步骤 5:日志、压缩和头
访问日志在机器人猛砸某条路径或你调试 404 时有帮助。可以按站点记录。如果托管浏览器应用,加上安全头;不要在不理解 HSTS 的情况下复制巨大的头包(一旦设置很长的 max-age,浏览器会记住)。
example.com {
root * /var/www/example
file_server
encode gzip
log {
output file /var/log/caddy/example.log
}
header {
X-Content-Type-Options nosniff
Referrer-Policy no-referrer-when-downgrade
-Server
}
}
mkdir -p /var/log/caddy
chown caddy:caddy /var/log/caddy
systemctl reload caddy步骤 6:PHP 和其他附加功能
Caddy 可以用 php_fastcgi 指令与 PHP-FPM 对话。对许多 WordPress 或 Laravel 主机这就够了,但你仍然需要安装 php-fpm 以及匹配的套接字路径。如果你已经有完美的 Nginx PHP 配置,一个晚上迁完是可选的——Caddy 更擅长反向代理 + 静态 + 自动 TLS。
example.com {
root * /var/www/example
php_fastcgi unix//run/php/php8.3-fpm.sock
file_server
}
# Confirm FPM is running:
systemctl status php8.3-fpm从 Nginx 迁移且没有停机意外
启动 Caddy 之前先停 Nginx,否则它们会抢 80/443。如果也在迁 IP,前一天降低 DNS TTL。用 curl --resolve 测试,这样你可以在切换 A 记录之前打到新 VPS。把 Nginx 配置在 git 里留一周,以防需要回滚。
- systemctl stop nginx && systemctl disable nginx
- 安装 Caddy,校验 Caddyfile,启动 Caddy
- curl -I --resolve example.com:443:NEW_IP https://example.com
- 仅当 IP 是新的时才改 DNS
- 重新签发是自动的;不要同时对同一主机名跑 Certbot
故障排除
ACME 失败几乎总是 DNS、防火墙,或端口 80 上的另一个服务。Caddy 日志行会提到 tls.obtain。如果站点在 HTTP 上能用但 HTTPS 不行,签发从未完成。如果看到太多证书,你撞上了 Let's Encrypt 速率限制——测试时用 staging CA,不要用生产。
- validate 失败:Caddyfile 语法,缺少大括号
- root 上 permission denied:给站点目录 chown caddy
- bind: address already in use — ss -tulpn | grep -E ':80|:443'
- 证书超时:从 VPS 上 dig +short 该域名,ufw allow 80
- 502 reverse_proxy:后端挂了,或你从容器网络错误地代理到 localhost
安全注意事项
Caddy 的 TLS 默认偏保守。你的工作是应用安全和 SSH。不要在公共接口上启用 Caddy 管理 API。默认管理端点是本地的;保持那样。如果使用网上的 Caddyfile,读每一个 matcher——把 /* 反向代理到内部 IP 的片段可以变成开放代理。
- 不要暴露管理 API
- 保持软件包更新
- 只有在所有主机名的 HTTPS 都确认能用之后才上 HSTS
- 分开测试和生产主机名以保护 ACME 速率限制
提示
- caddy fmt --overwrite /etc/caddy/Caddyfile 让文件保持可读
- 有很多相似站点时使用 import 片段
- 对于 Docker,带挂载 Caddyfile 的 caddy:alpine 镜像很常见
- 通配符证书需要 DNS 模块和 API 令牌——保护那个令牌
- 重写整个文件之前先读 journalctl -u caddy
VPS 上的 Caddy 是:官方软件包、一份短 Caddyfile、打开的端口 80/443、已经指向服务器的 DNS,以及编辑后的 systemd reload。把它当静态文件服务器,或 Gunicorn、Node、Docker 前面的反向代理。当 ACME 能在端口 80 上应答时,自动 HTTPS 就会工作;如果签发失败,先修 DNS 和端口冲突,再怪 Caddy。如果你经常重装,备份 /var/lib/caddy。