最近在研究 TLS 协议的 SNI(Server Name Indication)扩展机制时,产生了一个想法:能不能在自己的 VPS 上搭建一个拥有合法 TLS 证书的站点,让外部看起来就是一个普通的个人博客?
经过几天的折腾,我使用 Caddy + Docker 的方案完成了搭建。本文记录完整过程,供有同样需求的朋友参考。
为什么选择 Caddy
传统的方案是用 Nginx + Certbot 手动申请 Let's Encrypt 证书,但 Certbot 需要定期续签,配置也比较繁琐。Caddy 的最大优势是自动 HTTPS——它内置了 ACME 客户端,会在启动时自动向 Let's Encrypt 申请证书,并在证书即将过期时自动续签,完全不需要人工干预。
对比一下两种方案的配置复杂度:
| 特性 | Nginx + Certbot | Caddy |
|---|---|---|
| 自动申请证书 | 需要手动运行 certbot | 内置自动 |
| 自动续签 | 需要配置 cron | 内置自动 |
| 配置文件 | 约 30 行 | 约 5 行 |
| HTTP→HTTPS 跳转 | 需要手动配置 | 内置自动 |
环境准备
我使用的是一台日本 VPS,系统为 Ubuntu 22.04。你需要准备:
- 一个域名(建议使用子域名,如
blog.example.com) - 一台有公网 IP 的 VPS
- 已安装 Docker 和 Docker Compose
DNS 配置
在域名 DNS 管理处添加一条 A 记录,将子域名指向 VPS 的公网 IP。如果你使用 Cloudflare 管理 DNS,请确保代理状态为"仅 DNS"(灰色云朵),否则流量会经过 Cloudflare CDN,Caddy 无法完成 ACME 验证。
Caddy 配置文件
Caddy 的配置文件叫 Caddyfile,语法非常简洁:
blog.example.com {
tls your-email@example.com
root * /usr/share/caddy
file_server
}
只需把域名和邮箱替换成你自己的即可。tls 指令后面的邮箱是 Let's Encrypt 用于账户注册和到期提醒的。
Docker Compose 部署
使用 Docker 部署是最省心的方式,不需要在宿主机上安装 Caddy:
version: '3.8'
services:
caddy:
image: caddy:alpine
container_name: my-site
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./site:/usr/share/caddy:ro
- caddy_data:/data
restart: unless-stopped
volumes:
caddy_data:
启动后,Caddy 会自动完成以下操作:
- 监听 80 端口,响应 ACME HTTP-01 验证请求
- 向 Let's Encrypt 发起证书申请
- 获取证书后,自动启用 443 端口的 HTTPS
- 配置 HTTP→HTTPS 自动跳转
验证结果
容器启动后,可以通过以下命令查看日志,确认证书是否签发成功:
docker logs my-site 2>&1 | grep certificate
如果看到 certificate obtained successfully 字样,说明一切正常。在浏览器中访问 https://blog.example.com,应该能看到你的站点,并且地址栏显示安全锁标志。
常见问题
证书签发失败
最常见的原因是 80 端口不可达。请检查:
- VPS 防火墙是否放行了 80 和 443 端口
- 云服务商安全组是否放行了 80 和 443 端口
- DNS 是否已正确指向 VPS IP
- 如果用了 Cloudflare,是否关闭了代理(灰色云朵)
端口被占用
如果 VPS 上已经运行了 Nginx 或 Apache,80/443 端口会被占用。你需要先停止这些服务,或者修改 Caddy 的监听端口(但不建议,因为标准 HTTPS 需要 443)。
整个搭建过程不到 10 分钟,Caddy 的自动 HTTPS 功能确实大大简化了 TLS 站点的部署流程。如果你也有类似需求,强烈推荐试试这个方案。
有任何问题欢迎在评论区讨论,或者发邮件到 linyi@example.com。