首页 / 工具箱 / 服务器工具箱 / Caddy

Go 写的单二进制 Web 服务器,特点是默认自动 HTTPS、配置极简。1.6 GiB 服务器候选评估:内存、配置成本、与 nginx / traefik 的取舍。

解决什么问题

静态博客上线后,需要一个能挂 80/443 端口、能反代后端、能跑 HTTPS 的进程。我这台 1.6 GiB、无 Swap 的 Ubuntu 22.04 服务器上,目前是 hugo server 单进程直露 8080 端口,HTTPS 和域名都没有。一旦要补这一层,最直接的两个候选是 Nginx 和 Caddy,容器化场景里还会再搭一个 Traefik。这篇专门评估 Caddy:它能解决什么、解决得比同行好在哪里、又有哪些它解决不了的事。

Caddy 是什么

Caddy 是一个用 Go 写的、单文件可执行的 Web 服务器平台。最常见的用法是把它当作 HTTPS 反向代理或静态文件服务器。它有两个标志性特点:

  1. 默认就开 HTTPS——首次拿到合法域名解析后,Caddy 会自动向 ACME 提供商(默认 ZeroSSL,回退 Let’s Encrypt)申请证书,证书快过期前自动续期,不需要任何额外脚本。
  2. Caddyfile 配置极简——一个能完成「反代 + 压缩 + HTTPS + HTTP/3」的反向代理,Caddyfile 只需要 3–5 行;同样的事在 Nginx 通常要 20–40 行。

官方二进制完全静态链接,不依赖 libc,可以扔进 scratch 镜像里跑。这意味着在 1.6 GiB 的机器上,部署它就是 curl 下载 + chmod +x,没有 apt 依赖、没有动态库冲突。

跟 Nginx 比

维度CaddyNginx
配置文件Caddyfile(DSL)nginx.conf(指令 + 块)
HTTPS默认自动签发 + 续期需要 certbot --nginx 或手动配 ssl_certificate
配置体积静态站点 3–5 行静态站点 15–30 行
动态配置有 HTTP API,可热改需要 reload,不支持热改单条规则
HTTP/3(QUIC)默认开启需要额外编译或新版本
插件生态模块化但比 Nginx 小第三方模块极多
单进程 RSS(参考)~30 MiB 静态站点 / ~50 MiB 反代~10 MiB 静态 / ~30 MiB 反代
配置文件格式要求容错强,少一个 } 也能报错清晰缺 } 直接沉默启动,踩过坑
高并发极限中等极高(C10K 是 Nginx 的招牌)

数据来源:Caddy 官方 GitHub README「Production-ready after serving trillions of requests」;Nginx 单进程 RSS 是社区广泛引用的基准值,这台 1.6 GiB 服务器还没正式接入两者。

实操层面两者的差距主要在三件事:

  1. HTTPS 自动化:Nginx 必须配合 certbot 或自写脚本,Caddy 把这件事内化了,运维心智成本直接砍半。
  2. 配置可读性:Caddyfile 的写法贴近自然语言(encode gzip、file_server、reverse_proxy localhost:8080),新人半小时能上手;Nginx 的 location 匹配优先级、try_files 行为对新手不够友好。
  3. 配置可靠性:Nginx 配错时常「启动成功但行为异常」,Caddy 配错会直接 caddy adapt 拒绝并指出位置,CI 里可以拦下来。

跟 Traefik 比

Traefik 是容器化场景里的常青树:它和 Docker / Kubernetes / Docker Compose 深度集成,能自动从容器标签里读出域名、路径、TLS 信息,自己完成路由。Caddy 在这一点上明显落后——它要靠你手动写 Caddyfile,不会自己扫容器标签。

但反过来,Traefik 是「为编排而生」:

  • 它默认假设你跑很多服务、要自动发现;
  • 它有 Web UI、Prometheus 指标、复杂的 Provider 体系;
  • 单实例 RSS 通常 50–80 MiB,加上 etcd / Redis 之类的外部依赖会更多;
  • 配置是动态的(写进 KV),新手排错靠看标签和 dashboard,门槛在「理解它的 Provider 模型」。

对照场景:

场景更适合的工具
单台 VPS 跑 1–3 个站点,反代 + 静态Caddy(零依赖、配置短)
Docker Compose 起 5+ 个服务,要自动 HTTPSTraefik(自动化收益大)
Kubernetes IngressTraefik / Nginx Ingress
不想维护 certbot 脚本、只想写个配置就跑Caddy

这台机器上只有 Hugo + 可能再加一个 DSH,是典型的「1–3 个站点」场景,Caddy 的契合度比 Traefik 高。

一键 HTTPS 是怎么实现的

Caddy 的 HTTPS 自动化由 tls app + certmagic 库(其作者的另一个项目)联合完成,流程大致是:

  1. Caddyfile 里写了站点地址——比如 example.com { ... }。
  2. 首次收到请求——Caddy 解析该域名指向本机,触发 certmagic。
  3. ACME 流程——向 ZeroSSL(默认)或 Let’s Encrypt(回退)发起 HTTP-01 或 DNS-01 质询,自动监听 80 端口完成。
  4. 证书入库——证书和私钥默认存到 $HOME/.local/share/caddy,可改成 MySQL / PostgreSQL / 自定义存储。
  5. 续期——certmagic 启动一个后台 goroutine,每天检查一次即将过期的证书,提前自动续。

几个我踩过的细节:

  • HTTP-01 要求 80 端口能从公网访问。如果你用 Cloudflare 代理且开了「DNS-only」灰云,HTTP-01 能跑;如果开了橙色代理(HTTP/HTTPS 全代理),80 被 CF 占走,Caddy 的 HTTP-01 会失败,需要改用 DNS-01(配 Cloudflare API Token)。
  • 本地 IP / 内网域名不签发公共证书。127.0.0.1、192.168.x.x 这类 Caddy 会用自己的本地 CA 签一张自签证书,浏览器会报警——这是预期行为,不是 bug。
  • On-Demand TLS:在 tls.on_demand 模式下,Caddy 会第一次收到对陌生域名的请求时才去申请证书,避免大批量预签发撞 ACME 速率限制。需要谨慎打开,因为任何能访问 443 的人都能触发签发。

对我这台目前没有备案、没有域名的服务器来说,HTTPS 这条路暂时走不通——Caddy 再自动也没法给 IP 签公共证书。这一节是为备案后切换准备的。

Caddyfile 示例(脱敏 IP)

下面是「反代 Hugo + 自动 HTTPS + HTTP/3 + Gzip」的最小可用 Caddyfile。所有域名 / IP / 路径都已脱敏,请按你自己的替换。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 全局选项:仅监听 80 + 443,关闭 admin API 的对外暴露
{
    admin off
    auto_https off
    servers {
        protocols h1 h2 h3
    }
}

# 示例站点 1:纯静态文件
static.example.cn {
    root * /var/www/static
    encode gzip zstd
    file_server
}

# 示例站点 2:反代 Hugo server
blog.example.cn {
    reverse_proxy 127.0.0.1:8080 {
        header_up Host {host}
        header_up X-Real-IP {remote}
        header_up X-Forwarded-For {remote}
        header_up X-Forwarded-Proto {scheme}
    }
    encode gzip zstd
}

# 示例站点 3:内网服务,只允许本地子网访问
intranet.example.cn {
    @blocked not remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
    respond @blocked "403 forbidden" 403
    reverse_proxy 127.0.0.1:3080
}

启动方式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 前台跑(调试用)
caddy run --config /etc/caddy/Caddyfile

# 验证配置(不改运行状态)
caddy validate --config /etc/caddy/Caddyfile

# 转 JSON 看实际生效配置
caddy adapt --config /etc/caddy/Caddyfile

# 生产用 systemd 装为服务
caddy stop
sudo systemctl enable --now caddy

注意:Caddyfile 的缩进必须是 Tab,空格会被当成语法错误。第一次写很容易踩。

关键规格

  • 单二进制:官方 Linux amd64 二进制约 45 MB,启动后静态映射约 80 MB;
  • 常驻 RSS:纯 file_server 实测 25–35 MiB,加 reverse_proxy 约 40–60 MiB;
  • HTTPS 自动化:ZeroSSL / Let’s Encrypt 双签发,本地 CA 处理内网域名,OCSP Stapling 默认开;
  • 协议:HTTP/1.1、HTTP/2、HTTP/3(QUIC)默认全开;
  • 依赖:无 libc 依赖,无外部运行时,单文件即可启动;
  • API:HTTP API 可热改配置(默认监听 localhost:2019),CI 友好。

缺点限制

  • 需要合法域名才能用上 HTTPS。光有 IP 不行,ACME 不签发 IP 证书。本地 IP 用 Caddy 的内网 CA 是自签证书,浏览器报警。
  • 动态配置不如 Traefik 自动。多服务场景要手动维护 Caddyfile,没有「自动发现」一说。
  • 企业内网场景缺企业级特性:没有 HAProxy 那种细粒度的 TCP 健康检查 / rate-limit 模块,Caddy 的 rate_limit 是基础版。
  • 生态比 Nginx 小:第三方模块、waf、geoip 资料都要去 caddy.community 现找,Stack Overflow 上的 Nginx 答案质量碾压 Caddy。
  • 高并发极限比 Nginx 弱:Caddy 是 Go 的 goroutine 模型,单实例 10k+ 连接会撞 GC;Nginx 的事件循环在同样机器上能跑到 50k+。
  • 配置改完需要 reload 或 API 推送,不像 Nginx 可以用 nginx -s reload 那样无中断切换,但 Caddy 的热重载已经能做到零丢连接,比 Nginx 不差。

替代方案

  • Nginx:成熟、文档多、极限并发强,但 HTTPS 自动化要外挂 certbot,配置啰嗦;
  • Traefik:容器编排场景首选,自动发现 + 自动 HTTPS,单实例重;
  • HAProxy:TCP / UDP 反代王者,但没有自动 HTTPS,配置更接近系统层;
  • Caddy + xcaddy 自编译:需要私有插件时用 xcaddy build 把模块链进去,编译一次约 2–5 分钟。

我目前的选择

当前这台服务器没有部署 Caddy。原因是:

  1. 没有真实域名,HTTPS 跑不起来;
  2. 当前用 hugo server 直露 8080 端口已经够用,Hugo 自带文件监听、Caddyfile 写出来收益有限;
  3. 切到 Caddy 需要把 hugo 构建移到 cron 或 git hook,目前周更节奏下没动力做。

触发切换 Caddy 的条件(任一满足即启动):

  • 备案拿到域名,要上 HTTPS;
  • 加第二个站点(about / tools / api),需要统一反代;
  • Hugo server 的 --disableFastRender 让我写作时手动重启太烦,迁到「cron 重建 + Caddy 反代」架构。

预估切换成本:写 1 份 10–20 行 Caddyfile + 配 1 个 cron 重建任务 + systemd 化 Caddy,约 1–2 小时首次部署。

官方与文档

相关