Skip to content

中间件使用指南

Caddy 的中间件系统是其强大功能的核心,提供了模块化的请求处理能力。中间件按照特定顺序执行,每个中间件都可以修改请求或响应。

🔄 中间件执行顺序

Caddy 中间件有固定的执行顺序,了解这个顺序对于正确配置非常重要:

1. map                    # 变量映射
2. root                   # 设置根目录
3. header                 # 请求头处理
4. rewrite                # URL 重写
5. try_files              # 文件尝试
6. basicauth              # 基本认证
7. request_body           # 请求体处理
8. ratelimit              # 限流
9. reverse_proxy          # 反向代理
10. file_server           # 文件服务器
11. respond               # 直接响应

🗺️ 变量映射 (map)

基本映射

caddyfile
{
    map {remote_host} {backend} {
        ~^192\.168\.1\. localhost:3001    # 内网用户
        ~^10\.0\.0\.    localhost:3002    # VPN 用户
        default         localhost:3000    # 默认后端
    }
}

example.com {
    reverse_proxy {backend}
}

复杂映射

caddyfile
{
    # 根据 User-Agent 映射后端
    map {header.user-agent} {mobile_backend} {
        ~*mobile|android|iphone localhost:3001
        default                  localhost:3000
    }
    
    # 根据时间映射缓存策略
    map {time.now.hour} {cache_duration} {
        ~^(0[0-6]|2[2-3])$ 3600    # 夜间长缓存
        default            1800     # 白天短缓存
    }
}

example.com {
    header Cache-Control "max-age={cache_duration}"
    reverse_proxy {mobile_backend}
}

📁 根目录设置 (root)

动态根目录

caddyfile
example.com {
    # 根据子域名设置不同根目录
    @subdomain header Host ~^([^.]+)\.example\.com$
    root @subdomain /var/www/{re.subdomain.1}
    
    # 默认根目录
    root * /var/www/default
    
    file_server
}

条件根目录

caddyfile
example.com {
    # 移动端使用不同目录
    @mobile header User-Agent *Mobile*
    root @mobile /var/www/mobile
    
    # 默认目录
    root * /var/www/desktop
    
    file_server
}

🏷️ 请求头处理 (header)

请求头修改

caddyfile
example.com {
    # 添加请求头
    header {
        X-Forwarded-Proto {scheme}
        X-Real-IP {remote_host}
        X-Request-ID {uuid}
        
        # 删除敏感头
        -Authorization
        -Cookie
        
        # 条件添加头
        ?X-Debug-Mode "on"  # 仅在不存在时添加
    }
    
    reverse_proxy localhost:3000
}

响应头修改

caddyfile
example.com {
    # 安全头
    header {
        # HSTS
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        
        # 内容安全策略
        Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'"
        
        # 其他安全头
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        X-XSS-Protection "1; mode=block"
        
        # 隐藏服务器信息
        -Server
        -X-Powered-By
    }
    
    root * /var/www/html
    file_server
}

条件头处理

caddyfile
example.com {
    # 根据文件类型设置缓存头
    @static path *.css *.js *.png *.jpg *.gif
    header @static Cache-Control "public, max-age=31536000"
    
    @html path *.html
    header @html Cache-Control "public, max-age=3600"
    
    @api path /api/*
    header @api {
        Cache-Control "no-cache, no-store, must-revalidate"
        Access-Control-Allow-Origin "*"
        Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
    }
    
    root * /var/www/html
    file_server
}

🔄 URL 重写 (rewrite)

基本重写

caddyfile
example.com {
    # 移除 .html 扩展名
    rewrite /about /about.html
    rewrite /contact /contact.html
    
    # 使用正则表达式
    rewrite ^/blog/([0-9]+)$ /blog.php?id=$1
    
    # 条件重写
    @old_api path /api/v1/*
    rewrite @old_api /api/v2{path}
    
    root * /var/www/html
    file_server
}

高级重写

caddyfile
example.com {
    # 多语言重写
    @chinese header Accept-Language *zh*
    rewrite @chinese /zh{path}
    
    @english header Accept-Language *en*
    rewrite @english /en{path}
    
    # 移动端重写
    @mobile header User-Agent *Mobile*
    rewrite @mobile /mobile{path}
    
    # SEO 友好 URL
    rewrite ^/product/([^/]+)/?$ /product.php?slug=$1
    rewrite ^/category/([^/]+)/page/([0-9]+)/?$ /category.php?name=$1&page=$2
    
    root * /var/www/html
    file_server
}

📂 文件尝试 (try_files)

SPA 应用

caddyfile
spa.example.com {
    root * /var/www/spa
    
    # 尝试文件,如果不存在则返回 index.html
    try_files {path} {path}/ /index.html
    
    file_server
}

复杂文件尝试

caddyfile
example.com {
    root * /var/www/html
    
    # 多级文件尝试
    try_files {path} {path}.html {path}/index.html /404.html
    
    # 条件文件尝试
    @api path /api/*
    try_files @api {path} /api/index.php
    
    file_server
}

🔐 基本认证 (basicauth)

简单认证

caddyfile
admin.example.com {
    basicauth {
        admin $2a$14$hgl486...  # bcrypt 哈希密码
        user  $2a$14$xyz123...
    }
    
    root * /var/www/admin
    file_server
}

条件认证

caddyfile
example.com {
    # 仅对管理路径要求认证
    @admin path /admin/*
    basicauth @admin {
        admin $2a$14$hgl486...
    }
    
    # 内网用户免认证
    @internal remote_ip 192.168.0.0/16
    basicauth @admin {
        skip @internal
        admin $2a$14$hgl486...
    }
    
    root * /var/www/html
    file_server
}

📦 请求体处理 (request_body)

请求体限制

caddyfile
api.example.com {
    # 限制请求体大小
    request_body {
        max_size 10MB
    }
    
    # 不同路径不同限制
    @upload path /upload/*
    request_body @upload {
        max_size 100MB
    }
    
    @api path /api/*
    request_body @api {
        max_size 1MB
    }
    
    reverse_proxy localhost:3000
}

🚦 限流 (rate_limit)

基本限流

caddyfile
api.example.com {
    rate_limit {
        zone api {
            key {remote_host}
            events 100
            window 1m
        }
    }
    
    reverse_proxy localhost:3000
}

复杂限流策略

caddyfile
api.example.com {
    # 不同路径不同限流策略
    @public path /api/public/*
    rate_limit @public {
        zone public {
            key {remote_host}
            events 1000
            window 1h
        }
    }
    
    @private path /api/private/*
    rate_limit @private {
        zone private {
            key {header.authorization}
            events 100
            window 1m
        }
    }
    
    # 上传接口特殊限制
    @upload path /api/upload/*
    rate_limit @upload {
        zone upload {
            key {remote_host}
            events 10
            window 1m
        }
    }
    
    reverse_proxy localhost:3000
}

🔧 自定义中间件链

完整的中间件链

caddyfile
example.com {
    # 1. 变量映射
    map {header.user-agent} {device_type} {
        ~*mobile mobile
        ~*tablet tablet
        default  desktop
    }
    
    # 2. 根目录设置
    root * /var/www/{device_type}
    
    # 3. 请求头处理
    header {
        X-Device-Type {device_type}
        X-Request-ID {uuid}
        -Server
    }
    
    # 4. URL 重写
    @api path /api/*
    rewrite @api /api/v2{path}
    
    # 5. 文件尝试
    try_files {path} {path}.html {path}/index.html
    
    # 6. 基本认证
    @admin path /admin/*
    basicauth @admin {
        admin $2a$14$hgl486...
    }
    
    # 7. 请求体限制
    @upload path /upload/*
    request_body @upload {
        max_size 50MB
    }
    
    # 8. 限流
    rate_limit {
        zone main {
            key {remote_host}
            events 500
            window 1m
        }
    }
    
    # 9. 文件服务器
    file_server {
        hide .htaccess .env
        browse
    }
}

📊 中间件监控

日志记录

caddyfile
example.com {
    # 详细的访问日志
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 10
        }
        format json {
            time_format "2006-01-02T15:04:05.000Z07:00"
            message_key "msg"
            level_key "level"
            time_key "ts"
        }
        include http.request.headers
        include http.response.headers
    }
    
    # 添加追踪头
    header X-Request-ID {uuid}
    header X-Processing-Time {time.now.unix_nano}
    
    reverse_proxy localhost:3000 {
        header_down X-Response-Time {time.now.unix_nano}
    }
}

指标收集

caddyfile
example.com {
    # 启用指标端点
    metrics /metrics {
        disable_openmetrics
    }
    
    # 添加自定义标签
    header X-Server-Instance "server-1"
    header X-Version "v1.2.3"
    
    reverse_proxy localhost:3000
}

🎯 实际应用场景

API 网关中间件链

caddyfile
gateway.example.com {
    # 1. 限流
    rate_limit {
        zone gateway {
            key {header.x-api-key}
            events 1000
            window 1h
        }
    }
    
    # 2. 认证
    @authenticated header X-API-Key *
    respond @authenticated "API Key required" 401
    
    # 3. 请求头处理
    header {
        X-Gateway "Caddy"
        X-Request-ID {uuid}
        X-Forwarded-For {remote_host}
    }
    
    # 4. 路由重写
    @v1 path /v1/*
    rewrite @v1 /api/v1{path}
    
    @v2 path /v2/*
    rewrite @v2 /api/v2{path}
    
    # 5. 反向代理
    reverse_proxy localhost:3000 {
        header_up X-Original-URI {uri}
        header_down X-Response-Time {time.now.unix_nano}
    }
}

静态站点中间件链

caddyfile
blog.example.com {
    # 1. 压缩
    encode gzip zstd
    
    # 2. 安全头
    header {
        Strict-Transport-Security "max-age=31536000"
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
    }
    
    # 3. 缓存控制
    @static path *.css *.js *.png *.jpg *.gif *.woff *.woff2
    header @static Cache-Control "public, max-age=31536000, immutable"
    
    @html path *.html
    header @html Cache-Control "public, max-age=3600"
    
    # 4. URL 重写(移除 .html)
    @html_ext path *.html
    rewrite @html_ext {path_regexp ^(.*)\.html$ $1}
    
    # 5. 文件尝试
    try_files {path} {path}.html {path}/index.html
    
    # 6. 文件服务器
    root * /var/www/blog
    file_server {
        precompressed gzip br
    }
}

通过合理配置中间件链,您可以构建功能强大、性能优异的 Web 服务。接下来我们将学习安全配置。 🔧