自托管服务的 HTTPS,别再手搓 nginx + certbot 了:Caddy 自动证书 + DNS-01 泛域名实操
凡是玩过自托管(homelab、内网服务、小团队自建工具)的人,大概都经历过这套折磨:服务跑起来了,想挂个域名上 HTTPS。于是装 nginx,写一份反向代理配置,再装 certbot,申请 Let’s Encrypt 证书,配一个 cron 去续期。三个月后证书过期那天,你才发现 cron 那行路径写错了、或者 certbot 的 webroot 模式跟 nginx 配置打架,网站一片红。我折腾这套折腾到第三次的时候,终于决定把它换掉。结论先说:自托管的 HTTPS,用 Caddy + DNS-01,基本可以做到「配一次,再也不管」。 这篇把里面真正有用的几点讲清楚。## 一、Caddy 的自动 HTTPS 到底省了什么Caddy 最被低估的能力,是它默认就把整个 ACME 流程内置了。你不需要单独装 certbot、不需要写续期 cron。一份 Caddyfile 可以短到这样:caddyapp.example.com { reverse_proxy localhost:8080}就这三行。Caddy 启动时会自己:1. 向 Let’s Encrypt 申请 app.example.com 的证书;2. 证书存到本地、自动在到期前续期;3. 80 端口自动跳 443,配好现代 TLS。对比 nginx + certbot,你省掉的是:证书申请脚本、续期 cron、跳转规则、TLS 参数调优——这些全都不用写了。nginx 不是不能做,而是这些事它全要你手动拼,而 Caddy 把「正确的默认」当成了出厂设置。## 二、真正的杀手锏:DNS-01 给内网服务也发证书上面那种叫 HTTP-01 验证:Let’s Encrypt 会回头访问你的 80 端口确认域名归你。问题是——如果你的服务在内网呢? 公网根本访问不到你的 80 端口,HTTP-01 直接没法用。这是自托管最常见的卡点:NAS、内网管理后台、家里的 homelab,想要 HTTPS 却卡在验证这一步。解法是 DNS-01 验证:不验端口,改验「你能不能改这个域名的 DNS 记录」。Caddy 通过你的DNS 服务商 API(Cloudflare、阿里云、DNSPod 等)自动写一条 TXT 记录完成验证。全程不需要任何入站端口暴露到公网。更爽的是,DNS-01 支持泛域名证书(*.example.com)。申请一张泛域名证书,你内网所有子域名——grafana.lab.example.com、nas.lab.example.com、pad.lab.example.com——全部一张证书搞定,新加服务不用再单独申请。配置长这样:caddy*.lab.example.com { tls { dns cloudflare {env.CF_API_TOKEN} } # 按子域名路由到不同后端 @grafana host grafana.lab.example.com handle @grafana { reverse_proxy localhost:3000 } @nas host nas.lab.example.com handle @nas { reverse_proxy localhost:5000 }}注意 Caddy 官方二进制默认不带 DNS 插件,你得用对应 DNS 商的插件重新构建一个 Caddy(xcaddy build --with github.com/caddy-dns/cloudflare)。这一步是很多人卡住的地方——官方镜像跑 DNS-01 会报找不到 provider,得自己 xcaddy 或用带插件的镜像。## 三、会咬你的几个坑跑通之后,有几个点踩过才知道:### 1. 通配符证书的边界*.example.com 只覆盖一级子域名,不覆盖 a.b.example.com(两级)。要覆盖两级得单独申请 *.b.example.com。规划子域名层级的时候先想清楚。### 2. DNS API Token 的权限范围给 Caddy 的 DNS Token,务必只授权改 TXT 记录那一个 zone,别图省事给账号级全权 token。这个 token 等于你域名的部分控制权,泄漏了能被人签证书。### 3. Let’s Encrypt 有速率限制同一个主域名每周 50 张证书。正常用不会撞到,但你要是写脚本反复重建容器、每次都重新申请,很容易被限流到第二周才能再签。证书目录一定要持久化(挂卷),别让它每次重启都重新申请。### 4. WebSocket / gRPC 别忘了反代普通 HTTP 没问题,但你后面要是挂了带 WebSocket 的服务(很多管理后台的实时日志都用 WS),得确认反代正确转发 Upgrade 头。Caddy 的 reverse_proxy 默认支持,但自己写 nginx 配置的话这是经典翻车点。## 四、后来我把这套也打包了道理都懂,但每加一台机器、每挂一个新服务,都要重构建带插件的 Caddy、写 Caddyfile、配 DNS token、记哪个子域名指向哪个后端……还是烦。于是我把它——连同前面的 CI 构建、多机部署、容器管理——一起塞进了我那个 Go 单二进制部署工具 Pipewright 里:- 内置一个自构建的 Caddy 镜像(带主流 DNS 插件),开箱就能跑 DNS-01;- 在 Web 面板上点几下给服务绑域名、配泛域名证书、设路径路由,不用手写 Caddyfile;- 一个证书总览面板,所有域名的证书状态、到期时间一眼看完,不用再 openssl s_client 一个个查;- 顺手做了个我自己最想要的功能:Per-PR 预览环境——每开一个 PR 自动起一套带独立子域名的 环境,合了或关了自动回收。自托管也能有 Vercel 那种「每个 PR 一个预览链接」的体验。它本质就是把这篇讲的「Caddy 自动 HTTPS + DNS-01 泛域名」产品化,省掉手搓那一截。开源 MIT,单二进制、无运行时依赖(前端 embed.FS 打进去,默认 SQLite),定位个人开发者 / 小团队。仓库:https://github.com/huangchengsir/pipewright不过就算你不用它,这篇的核心还是那句话:**自托管的 HTTPS 别再手搓 nginx + certbot 了。**Caddy + DNS-01 + 泛域名证书,配一次就能忘掉它的存在。欢迎来提 issue 拍砖。
更多推荐



所有评论(0)