Caddy 从入门到精通 / 07 - 中间件 / Middleware
中间件 / Middleware
中间件是位于请求和响应之间的处理层,用于完成认证、压缩、限流、日志等横切关注点。
Middleware sits between request and response, handling cross-cutting concerns like authentication, compression, rate limiting, and logging.
🟢 基础 / Basics
压缩(Compression / Encode)
example.com {
encode gzip zstd
root * /var/www/html
file_server
}
encode 自动根据 Accept-Encoding 头选择压缩算法,对文本资源进行压缩。
基本认证(Basic Auth)
example.com {
basicauth /* {
# 密码:用 caddy hash-password 生成
# caddy hash-password --plaintext "mypassword"
admin $2a$14$Zkx19XLiW6VYouLRRfbb3OHOJGEqJ3p8fGk5ME3m1/RMvZQU5FhOS
}
reverse_proxy localhost:3000
}
生成密码哈希:
caddy hash-password --plaintext "mypassword"
请求头操作
example.com {
# 添加响应头
header X-App-Name "MyApp"
header X-Frame-Options "SAMEORIGIN"
# 删除响应头
header -Server
header -X-Powered-By
# 安全头组合
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "camera=(), microphone=(), geolocation=()"
}
reverse_proxy localhost:3000
}
日志
{
log {
output file /var/log/caddy/access.log
}
}
example.com {
log {
output file /var/log/caddy/example.log {
roll_size 10mb
roll_keep 10
}
}
reverse_proxy localhost:3000
}
🟡 进阶 / Intermediate
限流(Rate Limiting)
需要第三方插件 caddy-ratelimit:
{
order rate_limit before reverse_proxy
}
example.com {
rate_limit {
zone dynamic {
key {remote_host}
events 100
window 1m
}
zone static {
key {remote_host}
events 500
window 1m
}
}
@static_files path *.css *.js *.png *.jpg
handle @static_files {
root * /var/www/static
file_server
}
handle {
reverse_proxy localhost:3000
}
}
编译包含插件:
xcaddy build --with github.com/mholt/caddy-ratelimit
IP 白名单 / 黑名单
example.com {
# 仅允许内网访问
@admin {
remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
}
handle /admin/* {
@not_lan not remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
respond @not_lan 403
reverse_proxy localhost:9090
}
reverse_proxy localhost:3000
}
CORS 配置
example.com {
@cors_preflight {
method OPTIONS
header Origin *
}
@cors_api {
header Origin *
}
handle @cors_preflight {
header {
Access-Control-Allow-Origin "*"
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization"
Access-Control-Max-Age "86400"
}
respond "" 204
}
handle @cors_api {
header Access-Control-Allow-Origin "*"
reverse_proxy localhost:3000
}
handle {
reverse_proxy localhost:3000
}
}
日志格式自定义(JSON 格式)
{
log {
format json {
time_format "2006-01-02T15:04:05Z07:00"
}
output file /var/log/caddy/access.log {
roll_size 50mb
roll_keep 20
roll_keep_for 720h
}
}
}
example.com {
log {
format json {
time_format "2006-01-02T15:04:05Z07:00"
}
output file /var/log/caddy/example.log {
roll_size 100mb
roll_keep 10
}
}
reverse_proxy localhost:3000
}
JSON 格式日志更方便 ELK / Loki 等日志系统采集。
自定义日志字段
(log_custom) {
log {
format json {
time_format "2006-01-02T15:04:05Z07:00"
}
output file /var/log/caddy/{args[0]}.log {
roll_size 50mb
roll_keep 10
}
}
}
example.com {
import log_custom example
reverse_proxy localhost:3000
}
按条件记录日志
example.com {
@not_health {
not path /healthz /readyz /metrics
}
log {
format json
output file /var/log/caddy/access.log
}
# 只记录非健康检查的请求
reverse_proxy localhost:3000
}
注意:Caddyfile 中目前没有直接的「条件日志」语法,但可以通过 JSON API 实现。
追踪请求 ID
example.com {
header X-Request-ID {request_id}
reverse_proxy localhost:3000 {
header_up X-Request-ID {request_id}
}
}
{request_id} 是 Caddy 内置变量,每个请求自动生成唯一 ID。
🔴 高级 / Advanced
中间件执行顺序
Caddyfile 中中间件按声明顺序执行,但理解背后的模型很重要:
请求进入
→ encode(压缩 — 在响应阶段生效)
→ basicauth(认证)
→ header(请求头处理)
→ reverse_proxy(处理请求)
→ header(响应头处理)
→ 返回响应
对于 encode 这类"响应中间件",它实际上在请求阶段注册一个拦截器,在响应阶段执行压缩。
使用 route 强制顺序
example.com {
route {
# 1. 先认证
basicauth {
admin $2a$14$hash
}
# 2. 再限流
rate_limit { ... }
# 3. 再代理
reverse_proxy localhost:3000
}
}
route 块内严格按声明顺序执行。
order 指令控制全局顺序
{
order rate_limit before reverse_proxy
order basicauth before reverse_proxy
order encode after reverse_proxy
}
当使用第三方插件时,order 可能是必需的。
IP 拒绝列表(基于 CrowdsSec)
xcaddy build --with github.com/hslatman/caddy-crowdsec-bouncer
{
order crowdsec first
}
example.com {
crowdsec {
api_url http://127.0.0.1:8080
api_key {env.CROWDSEC_API_KEY}
}
reverse_proxy localhost:3000
}
CrowdSec 是一个去中心化的安全引擎,可以自动分析日志并封禁恶意 IP。
集成 Prometheus Metrics
xcaddy build --with github.com/mholt/caddy-events-prometheus
{
metrics
}
example.com {
reverse_proxy localhost:3000
}
:2019 {
metrics /metrics
}
暴露 Prometheus 格式的 metrics 端点。
请求体修改
example.com {
@post method POST
handle @post {
request_body {
max_size 10MB
}
reverse_proxy localhost:3000
}
}
限制请求体大小,防止大文件上传耗尽资源。
按 User-Agent 分流
example.com {
@mobile {
header_regexp Mobile User-Agent "(?i)(iphone|android|mobile)"
}
@desktop not header_regexp Mobile User-Agent "(?i)(iphone|android|mobile)"
handle @mobile {
root * /var/www/mobile
file_server
}
handle @desktop {
root * /var/www/desktop
file_server
}
}
静态文件 + 反向代理 + 认证组合
example.com {
# 公开静态资源
handle /public/* {
header Cache-Control "public, max-age=86400"
root * /var/www/static
file_server
}
# 需要认证的 API
handle /api/* {
basicauth {
admin $2a$14$hash
}
reverse_proxy localhost:8080
}
# 需要认证的管理后台
handle /admin/* {
basicauth {
admin $2a$14$hash
}
reverse_proxy localhost:9090
}
# 默认:SPA
handle {
root * /var/www/spa/dist
try_files {path} /index.html
file_server
}
}
小结 / Summary
| 层级 | 内容 |
|---|---|
| 🟢 基础 | encode 压缩、basicauth 认证、header 头操作、日志 |
| 🟡 进阶 | 限流、IP 黑白名单、CORS、JSON 日志、请求 ID |
| 🔴 高级 | 执行顺序模型、order 指令、CrowdSec、Prometheus、复杂组合 |