Caddy 操作手册

7 分钟
本页目录

从一次请求经过的入口、匹配、路径改写和上游开始定位。Caddyfile 能解析、配置已加载、线上响应符合预期,是三个分别验证的结果。

当前任务入口
反代 404 / 路径错误路由与前缀
修改配置上线校验、重载、实测
TLS / 客户端证书证书归属与 mTLS

上游收到的路径可能已不同

正在呈现知识画面
剥离前缀后,上游看见哪个 URL 路径

handle /api/* 保留路径;handle_path /api/* 剥离 /api。裸 /api 不满足 /api/*,会进入本示例的兜底分支。

完整站点示例:静态文件与 /api/* 分流。将站点块合并到实际 Caddyfile,域名、root 和上游按部署替换。

example.com {
    handle_path /api/* {
        reverse_proxy 127.0.0.1:3000
    }
    handle {
        root * /srv/www
        file_server
    }
    log
}

该例让 /api/users 到上游变成 /users;若应用本身挂在 /api,改用 handle /api/* 保留前缀。/api/* 不匹配裸 /api,需按应用设计单独重定向或匹配。handle 组互斥,兜底静态分支只处理未命中请求;file_server 不默认开启目录列表,需明确 browse。

先从 Caddy 所在环境直连上游

# 在 Caddy 主机请求改写后的上游路径
curl -v http://127.0.0.1:3000/users

容器部署时 127.0.0.1 指容器自身,需在同一网络命名空间测试实际可达地址。核对状态与正文,先确认上游本身正常。

# 经指定 Caddy 入口验证外部路径与 TLS
curl -v --resolve example.com:443:127.0.0.1 https://example.com/api/users

--resolve 的三项是名称、端口、入口 IP;这里与上一条是同一资源的两种路径。测试 /api/、/api 以及静态路径,检查边界是否符合应用设计。

# 按请求时间读取 Caddy 服务诊断
journalctl -u caddy -b -n 100 --no-pager

区分运行日志与 access log;log 指令的输出目的地可能被配置为文件,不一定全部在 journal。从 502 的 upstream error 再定位协议或网络。

两个 URL 对照改写后的同一资源。--resolve 同时保留 URL 中的主机名与 TLS SNI,替换的 IP 需为实际入口;只改 Host header 不等于改变 TLS SNI。不要用 -k 把 TLS 故障隐藏为“反代已正常”。

现象检查
502上游监听地址、协议、容器命名空间、连接/TLS 错误;先从 Caddy 所在环境访问上游
404哪一层返回、matcher 是否命中、前缀是否被剥离、静态 root 和权限
403文件访问、认证/授权及上游日志,不能只按 HTTP 码猜测归属

多个后端的最小配置如下。健康探针必须返回能代表“可接请求”的状态;探针正常不代表所有业务依赖正常,单机多端口也不是跨主机容灾。

替代站点示例:整个站点转发给多个后端。它替换前一个同域名块,不能直接追加成重复站点;如需只代理 /api/*,保留对应 matcher。

example.com {
    reverse_proxy 127.0.0.1:3000 127.0.0.1:3001 {
        lb_policy round_robin
        health_uri /health
        health_interval 10s
    }
}

先适配检查,再按实际服务重载

确认运行二进制、配置路径与服务身份

# 确认本次配置语法对应的 Caddy 版本
caddy version

插件还需 caddy list-modules 核对,安装了另一个二进制不代表服务正在使用它。

# 查 ExecStart、ExecReload、User 和实际配置位置
systemctl cat caddy.service

下文 /etc/caddy/Caddyfile 按实际替换。保存旧文件后编辑;三份配置示例各自是独立站点方案,不能把同域名块直接拼起来。

# 预览格式调整而不写入文件
caddy fmt --diff /etc/caddy/Caddyfile

确认后如需格式化再单独加 --overwrite;fmt 不是语义校验。

# 查看 Caddyfile 被适配后的 JSON 结构
caddy adapt --config /etc/caddy/Caddyfile --adapter caddyfile --pretty

核对路由顺序、match 与 rewrite;adapt 不启动监听,输出可能含机密配置。

# 加载并检查模块配置,尚不替换运行服务
caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile

尽量以实际服务身份和同一环境执行,检查文件可读性;失败先修复再进入 reload。

# 通过已核对的 ExecReload 应用新配置
sudo systemctl reload caddy.service

此例要求 unit 支持 reload 且指向当前文件。检查返回值与日志;重载拒绝时确认旧配置是否仍在服务,而不是继续重启。

# 确认服务状态,再重复真实路由请求
systemctl status caddy.service --no-pager

沿本页 /api/users、裸 /api、静态页和上游健康接口分别验证。需要回退时恢复旧文件,重新 validate / reload 并做同样请求。

fmt 是格式,adapt 是生成 JSON;validate 进一步加载/provision 模块,但仍不证明全部运行时依赖和网络路径。尽量用实际服务身份验证文件权限。确认 unit 的 ExecReload 指向当前文件后再 reload;不支持 reload 的自定义 unit 用部署规定的 caddy reload。保留上一版配置,失败时确认旧配置仍在服务,再修正或回退。

reload 支持配置切换,不承诺所有长连接都无感;WebSocket/流式连接的关闭与延迟策略需单独设置验证。Admin API 的 GET /config 可读实际 JSON,但可能含敏感配置;不要为了查看而把默认本机管理口暴露出去。标准 CLI 没有通用 list-certificates 子命令,用线上 TLS 与存储配置验证。

自动 HTTPS 与手工证书各有一个维护者

公开域名自动 HTTPS 需要正确 DNS、挑战可达性或 DNS 插件权限,以及可写且持久的证书存储。Caddy 可使用多个 issuer,不固定等于只用 Let’s Encrypt。手工 tls cert key 则需要外部续期、部署与 reload 链,见 证书。HTTP/3 还需实际入口 UDP 443 与客户端支持,TCP HTTPS 正常不能单独证明它可用。

独立 mTLS 站点示例:客户端 CA 文件必须存在且服务账户可读。配置发布按上节的校验与重载流程执行。

internal.example.com {
    tls {
        client_auth {
            mode require_and_verify
            trust_pool file /etc/caddy/client-ca.pem
        }
    }
    reverse_proxy 127.0.0.1:3000
}

适用于支持 trust_pool 的 Caddy 版本;client-ca.pem 是签发客户端证书的受信任 CA,不是吊销列表。mTLS 先验证证书,再由应用完成业务授权;不要把受信任客户端证书文件当“黑名单”。标准 reverse_proxy 已处理常用 X-Forwarded-*,有可信前置代理时配置 trusted_proxies,避免任意覆盖并信任外来 X-Forwarded-For。

mTLS 用正反两个请求验证

仅在上述 mTLS 站点配置已加载后执行:先不带客户端证书测试应被拒绝,再用受信任的客户端身份请求。CA 文件与客户端私钥应来自该站点批准的配置。

# 不带客户端证书,确认入口确实要求 mTLS
curl -v https://internal.example.com/

本例预期 TLS 拒绝;若正常返回业务内容,检查是否访问错入口或未加载 client_auth。私有服务器 CA 需加 --cacert,不能用 -k。

# 使用获准客户端证书验证可访问性
curl -v --cert client.crt --key client.key https://internal.example.com/

TLS 成功后仍检查应用授权结果;持有受信任证书不等于具有全部业务权限。

handle_path · reverse_proxy · CLI · TLS

边缘入口的实际配置另见 WildWindStudio EdgeOne runbook。