前言

第一次用 Caddy 配置反向代理时,会冒出一个很直接的感觉:这配置怎么能这么短。

因为很多时候,真正想解决的问题并不复杂,无非就是起一个本地 HTTP 服务验证页面或接口,给本地应用套一层反向代理,把某个站点通过域名暴露出去,再顺手把 HTTPS 也配好。

这些事情传统 Web Server 当然也能做,只不过在配置、证书、重载和默认行为上,往往还得自己多操一点心。

Caddy 的优势就在这里:默认配置更现代,配置语法更接近人的意图,而且自动 HTTPS 做得非常好。

Caddy 介绍

Caddy 介绍

https://caddyserver.com/

Caddy 是一个现代化的 Web Server,也可以把它理解成一个开箱即用的 HTTP 入口组件。

它最常见的几种用法包括:

  • 静态文件服务器:直接把某个目录暴露成网站;
  • 反向代理:把外部请求转发给本地或内网应用;
  • 自动 HTTPS 入口:自动申请和续期证书;
  • 本地开发网关:给多个本地服务统一挂一个域名或端口入口。

所以从定位上看,Caddy 不只是一个能返回网页的服务器,它更像一个轻量、现代、开箱即用的 Web 入口层。

相比 Nginx 那种偏指令式的配置风格,Caddy 的 Caddyfile 更接近一种声明:我要把哪个站点暴露出来,再对它做什么处理。

比如下面这个例子,即使第一次看,也大概能猜出来它在做什么:

1
2
3
4
:8080 {
    root * /var/www/html
    file_server
}

它的意思很直接:监听 8080 端口,把 /var/www/html 作为站点根目录,并启用静态文件服务。

Caddy 最出名的能力,就是自动 HTTPS。

只要你的域名解析正确、端口开放正常,Caddy 通常就能自动帮你申请和续期证书。对很多个人站点、小型服务或者内部工具来说,这意味着不用再手动折腾 certbot、定时续期、证书挂载这些额外工作。

这也是很多人第一次用完 Caddy 之后,就很难再回到传统证书配置流程的原因。

常用命令

校验配置

1
caddy validate --config Caddyfile

改配置之后先校验一下,能避免一些低级错误。

格式化配置

1
caddy fmt --overwrite Caddyfile

如果你的 Caddyfile 比较长,格式化一下会更整洁。

运行服务

1
caddy run

热加载配置

1
caddy reload --config Caddyfile

如果你已经有一个正在运行的 Caddy 实例,这个命令可以让它加载新的配置。

Caddy 功能

HTTP 服务

如果想快速起一个 HTTP Server,最简单的方式就是准备一个目录,然后让 Caddy 直接把它作为静态站点暴露出来。

假设我们有这样一个目录:

1
2
3
site/
├── index.html
└── app.js

接着在当前目录写一个最小 Caddyfile

1
2
3
4
:8080 {
    root * ./site
    file_server
}

然后执行:

1
2
caddy validate --config Caddyfile
caddy run

默认情况下,Caddy 会在当前目录查找 Caddyfile。启动成功后,访问 http://localhost:8080 就能看到页面。

这里涉及两个最核心的指令:

  • root * ./site:设置站点根目录;
  • file_server:启用静态文件服务。

反向代理

很多时候,我们用 Caddy 不是为了直接托管静态文件,而是为了给后端应用做一层统一入口。

比如你的应用实际跑在本机 3000 端口,现在希望用户访问 8080 时,由 Caddy 帮你转发过去:

1
2
3
:8080 {
    reverse_proxy 127.0.0.1:3000
}

这就是一个最基础的反向代理配置。

请求链路可以理解成:

1
Browser --> Caddy --> App Server

用户请求先到 Caddy,再由 Caddy 转发给后端应用。这样做的好处很直接:

  • 对外统一暴露入口;
  • 后端服务可以继续只监听本地端口;
  • 后续更容易补上 HTTPS、压缩、日志和访问控制;
  • 多个服务也更容易挂到同一个域名体系下。

如果从职责上看,Caddy 在这里扮演的其实就是一个入口层:外面的人访问它,它再决定把请求转给谁。

代理本地前后端服务

本地开发时,一个很常见的需求是:

  • 前端开发服务跑在 5173
  • 后端 API 跑在 8081
  • 希望对外统一通过一个域名访问。

这时候可以这样写:

1
2
3
4
5
6
7
8
9
localhost {
    handle /api/* {
        reverse_proxy 127.0.0.1:8081
    }

    handle {
        reverse_proxy 127.0.0.1:5173
    }
}

这个配置的含义非常直观:

  • /api/* 请求转发给后端接口服务;
  • 其它请求转发给前端开发服务。

如果之前总是靠前端脚手架自己配代理,这种方式会更接近真实线上入口,也更方便统一观察整个请求链路。

绑定域名与自动 HTTPS

如果你有一个域名,并且 DNS 解析到了这台机器,那么 Caddy 的优势就会一下子体现出来。

比如下面这个配置:

1
2
3
example.com {
    reverse_proxy 127.0.0.1:3000
}

很多情况下,这几行配置就足够了。Caddy 会尝试:

  • 监听 80443
  • 自动申请 TLS 证书,并自动完成证书续期;
  • 对外提供 HTTPS 服务。

这也是 Caddy 特别适合个人站点、小型服务、内部工具和轻量部署场景的重要原因。

不过要注意,自动 HTTPS 正常工作的前提通常包括:

  1. 域名已经正确解析到当前服务器;
  2. 80443 端口可以被外部访问;
  3. 机器网络环境允许完成 ACME 验证。

自动 HTTPS 原理

自动申请和续期证书,是 Caddy 最省心的能力之一。

如果把这个过程拆开来看,核心其实只有三步:

  1. 当你在配置里直接写域名时,Caddy 会识别出这个站点需要启用 HTTPS。
  2. 它会通过 ACME 协议向证书颁发机构申请证书,并通过 HTTP challenge 或 TLS challenge 之类的方式完成域名所有权验证。
  3. 证书签发成功后,Caddy 会自动加载证书,并在证书接近过期时提前完成续期。

也就是说,开发者通常不需要自己手动申请证书、手动拷贝证书文件,或者再额外写一个定时任务专门处理续期。

这套机制之所以好用,本质上是因为 Caddy 把原本分散的几件事情合到了一起:

  • 站点识别;
  • 证书申请;
  • 域名验证;
  • 证书加载;
  • 到期续期。

插件

总结

Caddy 之所以受欢迎,不是因为它把 Web Server 这件事变复杂了,而是因为它把很多常见场景做成了更自然的默认能力。

如果你只是想起一个 HTTP 服务,Caddy 很轻;如果你想顺手做反向代理和 HTTPS,Caddy 也很强。

对于个人站点、轻量服务、本地开发代理和中小型部署场景来说,Caddy 是一个非常值得掌握的工具。