跳转至

Caddy: 自动 HTTPS 的现代 Web 服务器

为什么要学 Caddy

配置 Nginx + Let's Encrypt SSL 证书是 Web 部署中最繁琐的环节之一:安装 certbot、配置定时续期、设置证书路径、处理证书过期报警……Caddy 把这一切变成了零配置

维度NginxCaddy
HTTPS 配置需要手动配置 certbot + 定时续期全自动(申请/续期/部署)
配置格式nginx.conf (复杂)Caddyfile (简洁) 或 JSON API
默认安全需要手动加固默认启用 HTTPS + 安全 Header
反向代理需要多行配置一行搞定
HTTP/2需要配置默认启用
HTTP/3需要额外编译默认支持
热重载nginx -s reloadAPI 热重载
语言CGo
插件扩展需要重新编译模块化加载

Caddy 的核心价值观:服务器应该默认安全,HTTPS 不应该是可选的


核心概念

白话解释

Caddy 是一个 Web 服务器,它自动做了三件传统需要手动配置的事情: 1. 自动 HTTPS:自动从 Let's Encrypt/ZeroSSL 申请证书,自动续期 2. 安全默认:HTTP 自动重定向到 HTTPS,启用现代 TLS 配置 3. 简单配置:一个域名 + 一个后端地址 = 完整的反向代理配置

核心概念表

概念说明Nginx 等价物
Caddyfile配置文件格式nginx.conf
Site Block一个站点的配置块server {}
Directive配置指令location / proxy_pass 等
Automatic HTTPS自动证书管理certbot + cron
Reverse Proxy反向代理proxy_pass
File Server静态文件服务root + location
Matcher请求匹配器location 匹配
Placeholder动态变量$variable
Admin API运行时管理 APInginx -s reload

Caddy vs Nginx 配置对比

反向代理示例:

# Nginx — 15+ 行
server {
    listen 80;
    server_name api.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name api.example.com;
    ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
# Caddy — 3 行
api.example.com {
    reverse_proxy localhost:3000
}

Caddy 自动处理了:SSL 证书申请/续期、HTTP→HTTPS 重定向、安全 Header、HTTP/2。


安装配置

安装方式

macOS

brew install caddy

Ubuntu/Debian

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Arch Linux

pacman -S caddy

Docker

docker run -d --name caddy \
  -p 80:80 -p 443:443 \
  -v caddy-data:/data \
  -v caddy-config:/config \
  -v $PWD/Caddyfile:/etc/caddy/Caddyfile \
  caddy:latest

基本配置

创建 Caddyfile(默认位置 /etc/caddy/Caddyfile):

# 最简配置:静态文件服务
localhost {
    root * /var/www/html
    file_server
}
# 启动 Caddy
caddy run --config Caddyfile

# 后台运行
caddy start --config Caddyfile

# 停止
caddy stop

# 重载配置
caddy reload --config Caddyfile

# 使用 systemd(推荐生产环境)
sudo systemctl enable --now caddy

快速上手

静态文件服务

# Caddyfile
localhost:8080 {
    root * /var/www/html
    file_server browse  # browse 启用目录浏览
}
# 快速命令行方式(不需要 Caddyfile)
caddy file-server --browse --listen :8080 --root /var/www/html

反向代理

# 单服务反向代理
api.example.com {
    reverse_proxy localhost:3000
}

# 多服务反向代理(按路径分发)
example.com {
    reverse_proxy /api/* localhost:3000
    reverse_proxy /ws/* localhost:3001
    root * /var/www/frontend
    file_server
}

多站点配置

# 多个域名,一个 Caddyfile
app.example.com {
    reverse_proxy localhost:3000
}

api.example.com {
    reverse_proxy localhost:8080
}

docs.example.com {
    root * /var/www/docs
    file_server
}

# 通配符域名
*.example.com {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
    reverse_proxy localhost:9000
}

本地开发(HTTP)

# 开发环境不需要 HTTPS
:8080 {
    reverse_proxy localhost:3000
}

# 或者使用 localhost(Caddy 会生成自签名证书)
localhost {
    reverse_proxy localhost:3000
}

进阶用法

负载均衡

example.com {
    reverse_proxy localhost:3001 localhost:3002 localhost:3003 {
        # 负载均衡策略
        lb_policy round_robin
        # 其他策略: random, first, least_conn, ip_hash

        # 健康检查
        health_uri /health
        health_interval 10s
        health_timeout 5s
        health_status 200

        # 失败重试
        lb_try_duration 5s
        lb_try_interval 250ms
    }
}
example.com {
    reverse_proxy localhost:3000

    # 添加安全 Header
    header {
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "strict-origin-when-cross-origin"
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        Content-Security-Policy "default-src 'self'"
        -Server   # 删除 Server header
    }
}

CORS 配置

api.example.com {
    reverse_proxy localhost:3000

    @cors_options method OPTIONS
    handle @cors_options {
        header Access-Control-Allow-Origin "*"
        header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
        header Access-Control-Allow-Headers "Content-Type, Authorization"
        header Access-Control-Max-Age "86400"
        respond "" 204
    }

    header Access-Control-Allow-Origin "*"
}

请求限流

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

基本认证

admin.example.com {
    basicauth {
        # 密码用 caddy hash-password 生成
        admin $2a$14$...hashed_password...
    }
    reverse_proxy localhost:9090
}
# 生成密码哈希
caddy hash-password
# 输入密码,获取哈希值

日志配置

example.com {
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 10
            roll_keep_for 720h
        }
        format json
        level INFO
    }
    reverse_proxy localhost:3000
}

WebSocket 代理

# WebSocket 自动支持,无需额外配置
example.com {
    reverse_proxy /ws localhost:3001
    reverse_proxy localhost:3000
}

压缩

example.com {
    encode gzip zstd  # 同时支持 gzip 和 zstd
    root * /var/www/html
    file_server
}

Docker Compose 完整示例

version: '3'

services:
  caddy:
    image: caddy:latest
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy-data:/data
      - caddy-config:/config
    restart: unless-stopped

  app:
    image: my-app:latest
    expose:
      - '3000'

  api:
    image: my-api:latest
    expose:
      - '8080'

volumes:
  caddy-data:
  caddy-config:
# Caddyfile
example.com {
    reverse_proxy /api/* api:8080
    reverse_proxy app:3000
}

Admin API

# Caddy 提供 REST API 进行运行时管理

# 获取当前配置
curl localhost:2019/config/

# 更新配置(不重启)
curl localhost:2019/load \
  -H "Content-Type: text/caddyfile" \
  --data-binary @Caddyfile

# 添加新路由
curl -X POST localhost:2019/config/apps/http/servers/srv0/routes \
  -H "Content-Type: application/json" \
  -d '...'

常见问题

Q1: Caddy 的自动 HTTPS 如何工作?

  1. Caddy 检测到配置中的域名
  2. 自动向 Let's Encrypt(或 ZeroSSL)申请证书
  3. 通过 HTTP-01 或 TLS-ALPN-01 验证域名所有权
  4. 下载并安装证书
  5. 在证书到期前自动续期

前提:域名的 DNS 指向 Caddy 服务器,且 80/443 端口可访问。

Q2: 内网服务怎么用 Caddy?

内网服务器没有公网访问,不能用 Let's Encrypt。选择: - 使用 localhost 或 IP(Caddy 生成自签名证书) - 使用内部 CA 签发证书 - DNS 验证方式(需要 Caddy DNS 插件)

192.168.1.100 {
    tls internal  # 使用内部 CA
    reverse_proxy localhost:3000
}

Q3: Caddy 性能和 Nginx 比怎么样?

  • 静态文件服务:Nginx 略快(C vs Go)
  • 反向代理:几乎无差异
  • 实际场景中,瓶颈几乎不在 Web 服务器层
  • Caddy 的优势是运维成本低(自动 HTTPS、简单配置)

Q4: 如何从 Nginx 迁移?

Caddy 没有自动迁移工具,但配置转换通常很简单: - server {} → 域名块 example.com {} - proxy_passreverse_proxy - root + locationroot * path + file_server - SSL 配置全删(Caddy 自动处理)

Q5: 如何使用通配符证书?

需要 DNS 验证,安装对应的 DNS Provider 插件:

# 安装带 Cloudflare DNS 插件的 Caddy
xcaddy build --with github.com/caddy-dns/cloudflare
*.example.com {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
    }
    reverse_proxy localhost:3000
}

参考资源

资源链接
官方网站https://caddyserver.com
GitHub 仓库https://github.com/caddyserver/caddy
Caddyfile 文档https://caddyserver.com/docs/caddyfile
指令参考https://caddyserver.com/docs/caddyfile/directives
JSON API 文档https://caddyserver.com/docs/json
社区插件https://caddyserver.com/download
xcaddy(自定义构建)https://github.com/caddyserver/xcaddy

总结:Caddy 是 Web 服务器领域"配置即安全"理念的代表。如果你厌倦了配置 Nginx SSL 证书的繁琐流程,Caddy 的自动 HTTPS 就是你需要的。对于中小型项目和个人部署,Caddy 的简洁配置和默认安全让部署变成了一件轻松的事。