Skip to content

Caddy2 配置详解

Caddy 提供了两种主要的配置方式:Caddyfile(简单易读)和 JSON(功能完整)。本章将详细介绍这两种配置方式的语法和使用方法。

📝 Caddyfile 基础

基本语法

Caddyfile 使用简洁的语法,每个站点配置以域名或地址开头:

caddyfile
# 基本站点配置
example.com {
    root * /var/www/html
    file_server
}

# 多个域名指向同一配置
example.com, www.example.com {
    root * /var/www/html
    file_server
}

# 本地开发
localhost:8080 {
    respond "Hello, World!"
}

全局配置块

全局配置使用花括号包围,放在文件开头:

caddyfile
{
    # 管理员邮箱(Let's Encrypt)
    email admin@example.com

    # 默认 SNI
    default_sni example.com

    # 管理端点
    admin localhost:2019

    # 日志配置
    log {
        output file /var/log/caddy/caddy.log
        format json
        level INFO
    }

    # 存储配置
    storage file_system {
        root /var/lib/caddy
    }

    # ACME CA 配置
    acme_ca https://acme-v02.api.letsencrypt.org/directory
    acme_ca_root /path/to/ca.pem

    # 本地 CA(开发环境)
    local_certs

    # 自动 HTTPS 配置
    auto_https off
    # auto_https disable_redirects
    # auto_https ignore_loaded_certs
}

站点地址格式

caddyfile
# 域名
example.com

# 带端口的域名
example.com:8080

# IP 地址
192.168.1.100

# IP 地址带端口
192.168.1.100:8080

# 通配符域名
*.example.com

# 多个地址
example.com, www.example.com, api.example.com

# 协议指定
http://example.com
https://example.com

# 路径匹配
example.com/api/*

🎯 常用指令

静态文件服务

caddyfile
example.com {
    # 设置根目录
    root * /var/www/html

    # 启用文件服务器
    file_server

    # 带选项的文件服务器
    file_server {
        hide .htaccess
        index index.html index.htm
        browse  # 启用目录浏览
    }

    # 压缩
    encode gzip zstd

    # 缓存头
    header {
        Cache-Control "public, max-age=3600"
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        X-XSS-Protection "1; mode=block"
    }
}

反向代理

caddyfile
api.example.com {
    # 基本反向代理
    reverse_proxy localhost:3000

    # 多个后端(负载均衡)
    reverse_proxy localhost:3000 localhost:3001 localhost:3002

    # 带选项的反向代理
    reverse_proxy localhost:3000 {
        # 健康检查
        health_uri /health
        health_interval 30s
        health_timeout 5s

        # 负载均衡策略
        lb_policy round_robin
        # lb_policy least_conn
        # lb_policy ip_hash
        # lb_policy first
        # lb_policy uri_hash

        # 请求头修改
        header_up Host {upstream_hostport}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}

        # 响应头修改
        header_down -Server

        # 超时设置
        transport http {
            dial_timeout 5s
            response_header_timeout 10s
        }
    }
}

路径匹配和重写

caddyfile
example.com {
    # 路径匹配
    handle /api/* {
        reverse_proxy localhost:3000
    }

    handle /static/* {
        root * /var/www/static
        file_server
    }

    # URL 重写
    rewrite /old-path /new-path
    rewrite /blog/* /posts/{path}

    # 重定向
    redir /old-page /new-page 301
    redir /temp-page /new-page 302

    # 条件重写
    @api path /api/v1/*
    rewrite @api /api/v2{path}

    # 默认处理
    handle {
        root * /var/www/html
        file_server
    }
}

认证和授权

caddyfile
admin.example.com {
    # 基本认证
    basicauth {
        admin $2a$14$hgl486...  # bcrypt 哈希
        user $2a$14$xyz123...
    }

    # 或者使用明文密码(不推荐生产环境)
    basicauth {
        admin JDJhJDE0JEhHTDQ4Ni...
    }

    root * /var/www/admin
    file_server
}

# JWT 认证(需要插件)
api.example.com {
    jwt {
        primary yes
        path /api
        redirect /login
        allow sub admin
        allow aud api
    }

    reverse_proxy localhost:3000
}

日志配置

caddyfile
example.com {
    # 访问日志
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 5
            roll_keep_for 720h
        }
        format json
        level INFO
    }

    # 错误日志
    log {
        output file /var/log/caddy/error.log
        format console
        level ERROR
    }

    root * /var/www/html
    file_server
}

🔧 高级配置

条件匹配器

caddyfile
example.com {
    # 路径匹配器
    @api path /api/*
    @static path /static/* /assets/*
    @images path *.jpg *.png *.gif

    # 方法匹配器
    @post method POST
    @get method GET

    # 头部匹配器
    @mobile header User-Agent *Mobile*
    @json header Content-Type application/json

    # 查询参数匹配器
    @debug query debug=true

    # 远程 IP 匹配器
    @internal remote_ip 192.168.0.0/16 10.0.0.0/8

    # 组合匹配器
    @api_post {
        path /api/*
        method POST
    }

    # 使用匹配器
    handle @api {
        reverse_proxy localhost:3000
    }

    handle @static {
        root * /var/www/static
        file_server
    }

    respond @mobile "Mobile version"
    respond @debug "Debug mode enabled"
}

中间件链

caddyfile
example.com {
    # 中间件按顺序执行

    # 1. 限流
    rate_limit {
        zone static {
            key {remote_host}
            events 100
            window 1m
        }
    }

    # 2. 安全头
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        X-XSS-Protection "1; mode=block"
        Referrer-Policy strict-origin-when-cross-origin
    }

    # 3. 压缩
    encode gzip zstd

    # 4. 缓存
    cache {
        ttl 1h
        stale 5m
    }

    # 5. 文件服务
    root * /var/www/html
    file_server
}

模板引擎

caddyfile
example.com {
    root * /var/www/templates

    templates {
        mime text/html
        between {{ }}
        root /var/www/templates
    }

    file_server
}

模板文件示例:

html
<!DOCTYPE html>
<html>
<head>
    <title>{{.Req.Host}}</title>
</head>
<body>
    <h1>Welcome to {{.Req.Host}}</h1>
    <p>Current time: {{now | date "2006-01-02 15:04:05"}}</p>
    <p>Your IP: {{.Req.RemoteAddr}}</p>
    <p>User Agent: {{.Req.Header.Get "User-Agent"}}</p>

    {{range .Req.URL.Query}}
        <p>{{.}}</p>
    {{end}}
</body>
</html>

🔄 JSON 配置

基本结构

JSON 配置提供了更精细的控制:

json
{
  "admin": {
    "listen": "localhost:2019"
  },
  "logging": {
    "logs": {
      "default": {
        "writer": {
          "output": "file",
          "filename": "/var/log/caddy/caddy.log"
        },
        "level": "INFO"
      }
    }
  },
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [":443"],
          "routes": [
            {
              "match": [
                {
                  "host": ["example.com"]
                }
              ],
              "handle": [
                {
                  "handler": "file_server",
                  "root": "/var/www/html"
                }
              ]
            }
          ]
        }
      }
    },
    "tls": {
      "automation": {
        "policies": [
          {
            "subjects": ["example.com"],
            "issuers": [
              {
                "module": "acme",
                "email": "admin@example.com"
              }
            ]
          }
        ]
      }
    }
  }
}

复杂路由配置

json
{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [":80", ":443"],
          "routes": [
            {
              "match": [
                {
                  "host": ["api.example.com"],
                  "path": ["/v1/*"]
                }
              ],
              "handle": [
                {
                  "handler": "reverse_proxy",
                  "upstreams": [
                    {"dial": "localhost:3000"},
                    {"dial": "localhost:3001"},
                    {"dial": "localhost:3002"}
                  ],
                  "load_balancing": {
                    "selection_policy": {
                      "policy": "round_robin"
                    }
                  },
                  "health_checks": {
                    "active": {
                      "uri": "/health",
                      "interval": "30s",
                      "timeout": "5s"
                    }
                  }
                }
              ]
            },
            {
              "match": [
                {
                  "host": ["example.com"]
                }
              ],
              "handle": [
                {
                  "handler": "file_server",
                  "root": "/var/www/html",
                  "index_names": ["index.html", "index.htm"]
                }
              ]
            }
          ]
        }
      }
    }
  }
}

🛠️ 配置管理

配置验证

bash
# 验证 Caddyfile
caddy validate --config /etc/caddy/Caddyfile

# 验证 JSON 配置
caddy validate --config /etc/caddy/caddy.json

# 格式化 Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile

配置转换

bash
# Caddyfile 转 JSON
caddy adapt --config /etc/caddy/Caddyfile --pretty

# 保存转换结果
caddy adapt --config /etc/caddy/Caddyfile > /etc/caddy/caddy.json

热重载

bash
# 重载配置(无中断)
caddy reload --config /etc/caddy/Caddyfile

# 使用 systemd
sudo systemctl reload caddy

# 通过 API 重载
curl -X POST "http://localhost:2019/load" \
  -H "Content-Type: application/json" \
  -d @/etc/caddy/caddy.json

配置文件分割

caddyfile
# 主配置文件 /etc/caddy/Caddyfile
{
    email admin@example.com
}

# 导入其他配置文件
import /etc/caddy/sites/*.caddy
import /etc/caddy/snippets/*.caddy
caddyfile
# /etc/caddy/sites/example.com.caddy
example.com {
    import common_headers
    root * /var/www/example.com
    file_server
}
caddyfile
# /etc/caddy/snippets/common_headers.caddy
header {
    X-Content-Type-Options nosniff
    X-Frame-Options DENY
    X-XSS-Protection "1; mode=block"
}

🔐 环境变量

在配置中使用环境变量

caddyfile
{
    email {$CADDY_EMAIL}
}

{$DOMAIN:localhost} {
    root * {$SITE_ROOT:/var/www/html}

    reverse_proxy {$BACKEND_HOST:localhost}:{$BACKEND_PORT:3000}

    basicauth {
        {$ADMIN_USER:admin} {$ADMIN_PASS_HASH}
    }
}

设置环境变量

bash
# 在 systemd 服务中
sudo systemctl edit caddy

# 添加环境变量
[Service]
Environment="CADDY_EMAIL=admin@example.com"
Environment="DOMAIN=example.com"
Environment="SITE_ROOT=/var/www/html"
Environment="BACKEND_HOST=localhost"
Environment="BACKEND_PORT=3000"

📊 配置最佳实践

安全配置

caddyfile
{
    # 安全的全局配置
    email {$CADDY_EMAIL}

    # 禁用管理 API(生产环境)
    admin off

    # 或限制管理 API 访问
    admin localhost:2019 {
        origins localhost:2019 127.0.0.1:2019
    }
}

example.com {
    # 安全头
    header {
        # HSTS
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

        # 内容类型嗅探保护
        X-Content-Type-Options nosniff

        # 点击劫持保护
        X-Frame-Options DENY

        # XSS 保护
        X-XSS-Protection "1; mode=block"

        # 引用策略
        Referrer-Policy strict-origin-when-cross-origin

        # 内容安全策略
        Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"

        # 隐藏服务器信息
        -Server
    }

    # 限制请求大小
    request_body {
        max_size 10MB
    }

    root * /var/www/html
    file_server
}

性能优化配置

caddyfile
{
    # 全局性能配置
    servers {
        protocol {
            experimental_http3
        }
    }
}

example.com {
    # 压缩
    encode {
        gzip 6
        zstd
        minimum_length 1024
        match {
            header Content-Type text/*
            header Content-Type application/json*
            header Content-Type application/javascript*
            header Content-Type application/xml*
        }
    }

    # 缓存头
    @static path *.css *.js *.png *.jpg *.gif *.ico *.woff *.woff2
    header @static {
        Cache-Control "public, max-age=31536000, immutable"
    }

    @html path *.html
    header @html {
        Cache-Control "public, max-age=3600"
    }

    # 预压缩文件
    file_server {
        precompressed gzip br
    }
}

开发环境配置

caddyfile
{
    # 开发环境全局配置
    local_certs
    log {
        level DEBUG
    }
}

localhost, *.localhost {
    tls internal

    @api path /api/*
    handle @api {
        reverse_proxy localhost:3000
    }

    handle {
        root * ./public
        file_server browse
    }
}

🔧 配置调试

查看当前配置

bash
# 查看当前运行的配置
curl http://localhost:2019/config/ | jq

# 查看特定部分
curl http://localhost:2019/config/apps/http | jq

配置测试

bash
# 测试配置文件
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile

# 干运行(不实际启动)
caddy validate --config /etc/caddy/Caddyfile

日志调试

caddyfile
{
    debug
    log {
        level DEBUG
        output file /var/log/caddy/debug.log
    }
}

掌握了这些配置技巧,您就可以灵活地配置 Caddy 来满足各种需求了!接下来让我们学习具体的应用场景。 🚀