Caddy 是一个 Web 服务器:一个 exe,配五行文本就能把目录挂成网站,还能当反向代理、自动申请证书。它把 Nginx 那些需要查文档才敢改的配置,压成了一份能读懂的 Caddyfile。
它解决的问题是「起个服务太麻烦」。装 Nginx 要改 conf、加站点,再重启看日志,Caddy 直接一条命令读配置就跑。
这次从解压到出页面全跑了一遍:版本命令 0.39 秒,配置转换 0.24 秒,服务起来 0.61 秒,50 次请求的中间延迟 0.9 毫秒。
一个 exe 当 Web 服务器
zip 18479498 字节,解开三个文件:caddy.exe、README.md 和一份 LICENSE。解压 0.33 秒,没有安装向导,不写注册表。
它是 Go 编译的单文件,不依赖 .NET 也不依赖 node。要开机自启,得自己做一个计划任务或者服务,它不自带这个功能。
| 文件 | 字节 |
|---|---|
| zip 包 | 18479498 |
| caddy.exe | 53159936 |
| README.md | 12661 |
| LICENSE | 11358 |
版本命令 0.39 秒
跑 caddy version 输出一行版本号加一串哈希:v2.11.7 h1:yj0Y4fYZGPkSvib…。后面那串是构建哈希,用来确认二进制没被人改过。
0.39 秒里大部分是进程启动。Go 的单文件程序都这样,冷启动几十毫秒到几百毫秒,跟文件大小有关。
五行配置换 34 行 JSON
Caddyfile 的语法是「地址 + 花括号 + 指令」。这份配置一共十行,去掉空行和括号,实际指令 4 条。
{
admin 127.0.0.1:2019
http_port 8082
}
:8082 {
root * D:/wb_cache/batch3/www
file_server browse
}
caddy adapt 把它翻译成 Caddy 真正执行的 JSON:492 字节、34 行。这个命令的价值在于能验证配置有没有写错,不启动服务也能查。
| 命令 | 耗时 | 输出 |
|---|---|---|
| caddy version | 0.39 秒 | v2.11.7 |
| caddy adapt | 0.24 秒 | 492 字节 / 34 行 JSON |
| caddy run 起来 | 0.61 秒 | 8082 端口可访问 |
| 目录页加载 | 0.05 秒 | 0.05 秒到可交互 |
http_port 这一行是为了避开 80
默认它会去抢 80 和 443。开发和测试机上这两个端口经常被占,所以在全局块里写 http_port 8082,把明文端口换掉。
不换也能跑,但一旦 80 被占,启动直接失败退出。日志里是一行 bind 错误,不会告诉你该改哪。
目录浏览页是自带的
file_server browse 这个指令打开目录列表。访问一个没有 index.html 的目录时,它会生成一个带搜索框的页面。
页面上有路径面包屑、文件数统计,还有 Name 和 Size 加上 Modified 三列,还能在 List 和 Grid 两种视图之间切。「3 files 14 KiB total」这行是它自己算的。

文件服务只要两行
root * 路径 指定根目录,file_server browse 打开列表。这两行合起来就是一个静态站点。
不带 browse 参数时,访问目录会返回 404,只有具体文件能拿到。想公开文件又不想让人翻目录,就写不带参数的版本。
性能:50 次请求 p50 0.9 毫秒
对首页连发 50 次请求,最快 0.8 毫秒,中位 0.9 毫秒,最慢 12.4 毫秒。最慢那次是首次建立的连接开销。
5 MB 的文件下载 0.01 秒完成,换算 618.7 MB/s。这是本地回环的数字,只说明它没有额外拷贝开销,不代表公网带宽。
| 指标 | 数值 |
|---|---|
| 首页体积 | 133 字节 |
| 50 次请求最快 | 0.8 毫秒 |
| 50 次请求中位 | 0.9 毫秒 |
| 50 次请求最慢 | 12.4 毫秒 |
| 5 MB 下载 | 0.01 秒 |
首次运行在 AppData 建了三个文件
第一次跑 caddy run,它在 C:\Users\用户名\AppData\Roaming\Caddy 建了目录,里面三个小文件。
autosave.json 存的是上一次成功启动的配置,268 字节。instance.uuid 是这个实例的唯一标识,36 字节。last_clean.json 记录上次清理证书存储的时间,110 字节。
| 文件 | 字节 | 作用 |
|---|---|---|
| autosave.json | 268 | 上次启动的配置快照 |
| instance.uuid | 36 | 实例标识 |
| last_clean.json | 110 | 证书存储清理时间 |
证书和密钥也在这里
开了 HTTPS 之后,申请到的证书存在同一个目录下的 certificates 子目录里。换机器时把这个目录拷走,就不用重新申请。
想整个搬走做绿色版,加 XDG_DATA_HOME 或者配置里指定 storage 路径,别让它写 AppData。
admin API 能在线改配置
配置里写了 admin 127.0.0.1:2019,起来之后 2019 端口就是一个管理接口。GET /config/ 返回当前生效的完整配置,往返 1.2 毫秒。
返回两个顶层键:admin 和 apps。apps 下面是 http 和 tls 两棵配置树。想改路由不用重启,直接 POST 到对应路径就行。
这个接口默认只监听本地。要远程管理得显式改成对外地址,同时会失去「本地免认证」的保护,得自己加一层防护。
几个真踩过的坑
| 症状 | 原因 | 处理 |
|---|---|---|
| 启动报 bind 错误直接退出 | 80 或 443 被占用 | 全局块设 http_port / https_port |
| 访问目录 404 | file_server 没加 browse | 补上 browse 参数 |
| 改了配置不生效 | 没重启或没调 reload | caddy reload 或重启进程 |
| 中文文件名乱码 | 系统编码与响应头不一致 | 确认浏览器与目录编码一致 |
| 配置改了没反应 | 用了别的 Caddyfile | 命令里显式 --config |
第一条最容易卡住新手。它不会提示「80 端口被占」,只报一个 bind 失败然后进程退出,用 netstat 查一遍才知道是谁占了。
什么场景值得上
| 你的情况 | 结论 |
|---|---|
| 把本地目录挂成网页给别人取 | 合适,两行配置 |
| 给内部服务加一层反向代理 | 合适,reverse_proxy 一行搞定 |
| 要自动 HTTPS | 合适,这是它的招牌能力 |
| 要图形界面点配置 | 不合适,它只有命令行和 API |
| 要 PHP 或 Java 应用服务器 | 不合适,它只做转发和静态文件 |
它的定位是「前面那一层」。静态文件、反向代理、证书续期和 HTTP 到 HTTPS 跳转,这些归它管。业务逻辑放后面的服务里。
写配置的手感比 Nginx 好,因为 Caddyfile 的层级浅。代价是生态小一些,遇到冷门需求查资料不如 Nginx 容易。







