2026 Caddy 自动 HTTPS 服务器:Docker 部署取代 Nginx 反代
要暴露自建服务到公网,反向代理是绕不开的一步。以前大家都在用 Nginx——功能强大没错,但每次加一个新服务都要写一大段 location 配置,手动申请 SSL 证书,设定时任务续期,再 reload nginx。重复了七八次之后就会想:有没有更省事的方法?
Caddy 就是来回答这个问题的。一个用 Go 写的 Web 服务器,核心卖点是自动 HTTPS ——你只需要告诉它「我要反代这个服务到这个端口」,它自动申请 Let’s Encrypt 证书、自动续期、自动配置 HTTPS 重定向。一行配置搞定之前 Nginx 十几行才能做的事。
GitHub 上 73.5k Star,社区极其活跃(今天还有新的 commit)。而且 Caddy 是 Go 写的单二进制,内存占用不到 30MB,在树莓派上都能跑得很欢。对比 Nginx 动辄 50-80MB 的内存,Caddy 对低配机器更友好。
一、Caddy vs Nginx vs Nginx Proxy Manager
| 对比项 | Caddy | Nginx | Nginx Proxy Manager |
|---|---|---|---|
| 语言 | Go | C | C + Node.js(前端面板) |
| 内存占用 | ~30MB | ~50-80MB | ~100-150MB |
| 自动 HTTPS | ✅ 内置 | ❌ 需 Certbot 等 | ✅ 内置 |
| 配置语法 | Caddyfile(简洁) | nginx.conf(复杂) | Web 图形界面 |
| 监听端口 | 80+443 | 80+443 | 80+443 + 81(管理面板) |
| 开源 | ✅ Apache 2.0 | ✅ 2-clause BSD | ✅ MIT |
| 插件生态 | 模块化加载 | 编译时静态链接 | 基于 Nginx 生态 |
| HTTP/3 | ✅ 默认支持 | ❌ 需额外编译 | ❌ |
选型建议:
- 如果只反代 3-5 个服务,用 Caddy 最省事——配置最短,自动 HTTPS 最省心
- 如果需要在 Web 界面上点来点去管理,用 NPM——可视化操作门槛最低
- 如果对性能有极致要求(每秒上万请求的网关),用 Nginx——C 写的性能天花板更高
但对个人自建场景(5-15 个服务,低配 VPS),Caddy 是最优解——内存最低、配置最简单、HTTPS 全自动。
二、Docker Compose 部署
创建目录:
mkdir -p ~/caddy && cd ~/caddy新建 docker-compose.yml:
services: caddy: image: caddy:latest container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./Caddyfile:/etc/caddy/Caddyfile - ./data:/data - ./config:/config environment: TZ: Asia/Shanghai端口说明:
80:HTTP,用于 Let’s Encrypt 域名验证和 HTTP 自动跳转到 HTTPS443:HTTPS 流量443/udp:HTTP/3(QUIC),可以显著提升移动端弱网环境下的加载速度
目录说明:
Caddyfile:Caddy 的配置文件,放 Docker Compose 旁边方便编辑data:Caddy 存储 SSL 证书和 ACME 账号信息的位置。这个目录很重要——证书到期前 Caddy 会自动续期,证书数据存在这里。如果删了 data 目录,所有域名的证书都要重新申请(有频率限制)config:运行时配置,一般不用管
启动服务:
docker compose up -d然后用 docker compose logs 检查启动状态——如果看到 successfully obtained certificate 的日志,说明证书申请成功了。
三、Caddyfile 配置实战
Caddyfile 的格式比 Nginx 配置简洁得多。先建一个基础的 Caddyfile:
# 全局配置段{ email your@email.com}
# 默认 404 页面:80 { respond "Caddy is running!"}把这个文件放在 ~/caddy/Caddyfile 里,然后 docker compose restart。访问你的服务器 IP 或域名,如果看到 “Caddy is running!” 说明基础部署成功。
下面是一些常见场景的配置:
场景一:反代单服务
你的域名.com { reverse_proxy 目标服务IP:端口}比如把本站的 Navidrome:
music.你的域名.com { reverse_proxy 192.168.1.100:4533}Caddy 自动为 music.你的域名.com 申请 Let’s Encrypt 证书,自动配置 HTTPS,自动将 HTTP 访问重定向到 HTTPS。这些步骤如果手动做,至少需要 Nginx 配置的 10-15 行加上 Certbot 的安装和定时任务。
场景二:反代多服务
Caddy 支持一个配置管理多个域名,非常直观:
# 密码管理器vault.你的域名.com { reverse_proxy vaultwarden:8080}
# 音乐流媒体music.你的域名.com { reverse_proxy navidrome:4533}
# Git 仓库git.你的域名.com { reverse_proxy gitea:3000}
# 文件存储minio.你的域名.com { reverse_proxy minio:9000}只要 Caddy 容器和这些服务容器在同一个 Docker 网络里,直接用容器名(如 vaultwarden:8080)就可以访问,不需要写 IP 地址。
场景三:静态文件服务器
files.你的域名.com { root * /srv/files file_server browse}用 Caddy 当文件服务器,浏览器打开能看到文件列表,点击直接下载。
场景四:添加基础认证
admin.你的域名.com { basicauth { 用户名 $2a$14$...加密后的密码哈希 } reverse_proxy admin-service:8080}密码哈希可以用 caddy hash-password 命令生成。这给管理后台加了一道简单的用户名密码认证,防君子也防脚本。
四、Docker 网络配置
前两篇文章(Navidrome 和 Gitea)里的 docker-compose.yml 如果各自独立运行,Caddy 默认没法通过容器名找到它们。有两种方式解决:
方案 A:共享 Docker 网络(推荐)
在 Caddy 启动之前,先创建一个共享网络:
docker network create selfhosted然后给每个服务的 docker-compose.yml 加上:
services: navidrome: # ...原有配置... networks: - selfhosted
networks: selfhosted: external: trueCaddy 也加入同一个网络:
services: caddy: # ...原有配置... networks: - selfhosted
networks: selfhosted: external: true这样 Caddy 的 reverse_proxy navidrome:4533 就能解析到 Navidrome 容器。容器名在同一 Docker 网络中会自动做 DNS 解析——Caddy 通过 navidrome:4533 就能找到对应的容器和端口,不需要记住任何 IP 地址。
这种方式的好处是每个服务独立管理,互不干扰,想停某个服务只需要进它的目录执行 docker compose down。
方案 B:使用 host 网络模式
最简单粗暴的方式——如果所有服务都在宿主机上暴露了端口(如 Navidrome 在宿主机 4533 端口),Caddy 可以直接用 localhost:4533 或 127.0.0.1:4533 反代:
music.你的域名.com { reverse_proxy 127.0.0.1:4533}但这种方式只适用于所有服务都在同一台机器上、而且都绑定了宿主机端口的情况。如果服务在别的机器上,还是要用方案 A 或者直接用 IP。
五、Caddy API 热重载
Nginx 改完配置要 nginx -s reload,Caddy 可以不用重启就加载新配置。Caddy v2 内置了 REST API,让管理配置变成发送 HTTP 请求。
加载新配置:
curl -X POST "http://localhost:2019/load" \ -H "Content-Type: text/caddyfile" \ -d '你的域名.com { reverse_proxy localhost:8080}'前提是 Caddy 的 API 端口(默认 2019)不对外暴露。在生产环境中,应该只允许本地访问或者加上认证。
查看当前配置:
curl http://localhost:2019/config/通过 docker exec 热重载 Caddyfile:
docker exec -w /etc/caddy caddy caddy reload这个命令会让 Caddy 重新读取 /etc/caddy/Caddyfile,应用新配置。修改了 Caddyfile 后运行这条命令即可,不需要 docker compose restart。如果配置有语法错误,Caddy 会拒绝加载并在日志里报错——不会像 Nginx 那样配置错了直接挂掉。
六、与本站其他服务联动
Caddy 部署完成后,可以和前面写的 Navidrome、Gitea、Vaultwarden 一起工作。一个完整的 Caddyfile 大概长这样:
{ email admin@你的域名.com}
# 监控面板status.你的域名.com { reverse_proxy uptime-kuma:3001}
# 文件存储files.你的域名.com { root * /srv/files file_server browse}
# 默认转到仪表盘你的域名.com { redir https://status.你的域名.com}所有域名统一走 Caddy 的 80/443 端口,每个子域名映射到对应的服务。这样做的好处是:
- 统一入口——只需要在防火墙开放 80 和 443 两个端口
- 统一证书——Caddy 自动为每个域名申请和续期证书
- 统一日志——所有流量日志都在 Caddy 的日志里,排查问题一目了然
- 统一升级——Caddy 是一个独立服务,升级不会影响其他容器
延伸阅读
- Docker 换源避坑指南 — 部署前的必备准备
- Nginx Proxy Manager 反代面板 — 统一管理所有服务的访问入口
- Uptime Kuma 自建服务监控 — 所有服务状态一目了然
七、常见问题
Q:Caddy 可以替代 Nginx 的所有功能吗?
不能。Nginx 有 20 年的沉淀,在高并发性能优化、负载均衡策略、gRPC 代理、stream 模块等方面比 Caddy 更成熟。Caddy 覆盖了个人自建场景 95% 以上的需求,但如果你的场景是「每秒一万请求的 API 网关」或者「复杂的七层负载均衡」,Nginx 仍然是更稳妥的选择。
Q:Let’s Encrypt 证书申请有频率限制吗?
有的。Let’s Encrypt 的限制是每 3 小时同一个域名最多申请 5 次、每周最多 50 次。Caddy 有自动重试和缓存机制,正常使用完全不用担心。但如果反复删除 data 目录导致 Caddy 重新申请证书,可能会触发限流。
Q:Caddy 能反代 WebSocket 吗?
可以,默认不需要额外配置。Caddy 的 reverse_proxy 直接支持 WebSocket 和 HTTP/2 的升级。Gitea 的实时通知、Vaultwarden 的密码同步、Jellyfin 的播放流——这些依赖 WebSocket 的服务都能被 Caddy 正确地反代。
Q:Caddy 和 Cloudflare Tunnel 怎么选?
Caddy 是在服务器上暴露端口、Caddy 负责证书和反代。Cloudflare Tunnel 是在服务器上跑一个 tunnel 客户端,不暴露任何端口,流量通过 Cloudflare 的网络进入。如果你有域名且服务器有公网 IP,用 Caddy 就够了。如果服务器在 NAT 后面(比如家里宽带没有公网 IP),Cloudflare Tunnel 是更好的选择。两者也可以组合使用——Caddy 做内部反代和证书,Cloudflare Tunnel 做入口。
Q:Caddy 支持 HTTP/3 吗?
默认支持。Caddy 从 v2.6 开始直接内置了 HTTP/3 支持,在 Docker 部署时只需要加一行 443:443/udp 端口映射就行。HTTP/3 基于 QUIC 协议,在弱网(移动网络、Wi-Fi 信号差)环境下连接速度比 HTTP/2 快很多。Caddy 会自动检测客户端是否支持 HTTP/3,不支持的回退到 HTTP/2 或 HTTP/1.1。
部署 Caddy 之后,你所有的自建服务终于有了统一的入口和管理方式。之前可能每个服务都暴露一个端口(3000、4533、8080……),现在只需要开 80+443 两个端口,Caddy 自动处理证书、反代和重定向。
Go 写的单二进制不到 30MB 内存,在 1 核 1G 的机器上跟其他服务共存毫无压力。配合 Navidrome、Gitea、Vaultwarden 这三篇文章,你已经有了一套完整的自建服务矩阵——音乐、代码、密码全部在自己的服务器上,通过 Caddy 统一入口和 HTTPS 保护。
推荐指数:⭐⭐⭐⭐⭐
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!