SSL/HTTPS 配置 #

概述 #

Caddy 最显著的特点是自动 HTTPS,它会自动获取、续期和管理 SSL 证书,无需手动配置。

text
┌─────────────────────────────────────────────────────────────┐
│                  Caddy 自动 HTTPS 流程                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 检测域名配置                                            │
│       ↓                                                     │
│  2. 自动申请 Let's Encrypt 证书                              │
│       ↓                                                     │
│  3. 自动配置 HTTPS                                          │
│       ↓                                                     │
│  4. 设置 HTTP → HTTPS 重定向                                │
│       ↓                                                     │
│  5. 自动续期证书                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

自动 HTTPS #

基本配置 #

caddyfile
# 只需指定域名,Caddy 自动处理 HTTPS
example.com {
    respond "Hello, HTTPS!"
}

Caddy 会自动:

  • 获取 Let’s Encrypt 证书
  • 配置 HTTPS 监听
  • 设置 HTTP 到 HTTPS 重定向
  • 自动续期证书

多域名证书 #

caddyfile
# 多个域名共享证书
example.com, www.example.com, api.example.com {
    respond "Multiple domains"
}

禁用自动 HTTPS #

caddyfile
# 禁用自动 HTTPS
example.com {
    tls off
    respond "HTTP only"
}

# 或使用端口指定
http://example.com {
    respond "HTTP only"
}

证书获取方式 #

HTTP 验证(默认) #

caddyfile
# 默认使用 HTTP-01 验证
example.com {
    respond "HTTP-01 challenge"
}

要求:

  • 服务器必须监听 80 端口
  • 域名必须解析到服务器 IP
  • 防火墙允许 80 端口访问

TLS 验证 #

caddyfile
# 使用 TLS-ALPN-01 验证
example.com {
    tls {
        protocols tls1.2 tls1.3
    }
    respond "TLS-ALPN-01 challenge"
}

要求:

  • 服务器必须监听 443 端口
  • 域名必须解析到服务器 IP
  • 防火墙允许 443 端口访问

DNS 验证 #

适用于内网或无法通过 80/443 端口验证的场景:

caddyfile
# Cloudflare DNS 验证
*.example.com {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }
    respond "Wildcard certificate"
}

DNS 提供商配置 #

caddyfile
# Cloudflare
*.example.com {
    tls {
        dns cloudflare {
            api_token {env.CLOUDFLARE_API_TOKEN}
        }
    }
}

# 阿里云 DNS
*.example.com {
    tls {
        dns alidns {
            access_key_id {env.ALIYUN_ACCESS_KEY_ID}
            access_key_secret {env.ALIYUN_ACCESS_KEY_SECRET}
        }
    }
}

# 腾讯云 DNS
*.example.com {
    tls {
        dns tencentcloud {
            secret_id {env.TENCENTCLOUD_SECRET_ID}
            secret_key {env.TENCENTCLOUD_SECRET_KEY}
        }
    }
}

# DigitalOcean
*.example.com {
    tls {
        dns digitalocean {env.DO_AUTH_TOKEN}
    }
}

# AWS Route 53
*.example.com {
    tls {
        dns route53 {
            access_key_id {env.AWS_ACCESS_KEY_ID}
            secret_access_key {env.AWS_SECRET_ACCESS_KEY}
        }
    }
}

通配符证书 #

基本配置 #

caddyfile
# 通配符证书需要 DNS 验证
*.example.com {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }
    respond "Wildcard domain"
}

通配符 + 主域名 #

caddyfile
# 同时覆盖主域名和子域名
example.com, *.example.com {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }
    
    # 根据子域名路由
    @api host api.example.com
    handle @api {
        reverse_proxy localhost:3000
    }
    
    @www host www.example.com
    handle @www {
        root * /var/www/html
        file_server
    }
    
    handle {
        respond "Main domain"
    }
}

自定义证书 #

使用自有证书 #

caddyfile
example.com {
    tls /etc/ssl/certs/example.com.crt /etc/ssl/certs/example.com.key
    respond "Custom certificate"
}

证书和密钥文件 #

caddyfile
example.com {
    tls {
        # 证书文件
        certificate /etc/ssl/certs/example.com.crt
        
        # 密钥文件
        key /etc/ssl/private/example.com.key
    }
    respond "Custom certificate"
}

PEM 格式内联 #

caddyfile
example.com {
    tls {
        certificate {
            format pem
            content "-----BEGIN CERTIFICATE-----\n..."
        }
        key {
            format pem
            content "-----BEGIN PRIVATE KEY-----\n..."
        }
    }
    respond "Inline certificate"
}

证书存储 #

默认存储位置 #

text
# Linux
/var/lib/caddy/.local/share/caddy/certificates/

# macOS
~/Library/Application Support/Caddy/certificates/

# Windows
%AppData%\Caddy\certificates\

自定义存储位置 #

caddyfile
{
    # 全局配置
    storage file_system {
        root /data/caddy
    }
}

example.com {
    respond "Custom storage"
}

分布式存储 #

caddyfile
{
    # 使用 Consul 存储(需要插件)
    storage consul {
        address consul.example.com:8500
        token {env.CONSUL_TOKEN}
        prefix caddy
    }
}

example.com {
    respond "Consul storage"
}

证书管理 #

查看证书 #

bash
# 通过 API 查看证书
curl localhost:2019/certificates | jq

# 输出示例
{
    "certificates": [
        {
            "domains": ["example.com"],
            "issuer": "Let's Encrypt",
            "not_before": "2024-01-01T00:00:00Z",
            "not_after": "2024-04-01T00:00:00Z"
        }
    ]
}

手动续期 #

bash
# Caddy 自动续期,但也可以手动触发
# 重载配置会检查证书是否需要续期
caddy reload --config Caddyfile

证书撤销 #

bash
# 撤销证书(需要通过 API 或重新配置)
# 删除存储的证书后重新启动
rm -rf /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/example.com/
caddy reload --config Caddyfile

TLS 配置选项 #

协议版本 #

caddyfile
example.com {
    tls {
        # 指定最小和最大协议版本
        protocols tls1.2 tls1.3
    }
    respond "TLS protocols"
}

加密套件 #

caddyfile
example.com {
    tls {
        # 指定加密套件
        ciphers TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    }
    respond "Custom ciphers"
}

曲线设置 #

caddyfile
example.com {
    tls {
        # 指定椭圆曲线
        curves x25519 secp256r1 secp384r1
    }
    respond "Custom curves"
}

完整 TLS 配置 #

caddyfile
example.com {
    tls {
        # 协议版本
        protocols tls1.2 tls1.3
        
        # 加密套件
        ciphers TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
                TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
                TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
                TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        
        # 曲线
        curves x25519 secp256r1 secp384r1
        
        # ALPN
        alpn h2 http/1.1
        
        # 客户端认证(可选)
        client_auth {
            mode require_and_verify
            trusted_ca_file /etc/ssl/ca.crt
        }
    }
    respond "Full TLS config"
}

客户端证书认证 #

要求客户端证书 #

caddyfile
example.com {
    tls {
        client_auth {
            # 模式:request | require | verify_if_given | require_and_verify
            mode require_and_verify
            
            # 信任的 CA 证书
            trusted_ca_file /etc/ssl/ca.crt
            
            # 可选:信任的证书列表
            trusted_leaf_file /etc/ssl/client.crt
        }
    }
    respond "Client cert required"
}

客户端证书模式 #

模式 说明
request 请求证书,但不强制
require 要求证书,但不验证
verify_if_given 如果提供则验证
require_and_verify 要求并验证证书

获取客户端证书信息 #

caddyfile
example.com {
    tls {
        client_auth {
            mode require_and_verify
            trusted_ca_file /etc/ssl/ca.crt
        }
    }
    
    # 获取客户端证书信息
    respond "Client CN: {tls_client_subject}"
}

本地开发 HTTPS #

使用自签名证书 #

caddyfile
localhost {
    tls internal
    respond "Local HTTPS"
}

信任本地证书 #

bash
# 安装本地 CA 到系统信任库
caddy trust

# 或手动信任
# macOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ~/Library/Application\ Support/Caddy/certificates/local/localhost.crt

开发环境配置 #

caddyfile
{
    # 本地开发使用内部 CA
    local_certs
}

# 本地项目
project1.localhost {
    tls internal
    reverse_proxy localhost:3001
}

project2.localhost {
    tls internal
    reverse_proxy localhost:3002
}

# 使用自定义域名(需要修改 hosts 文件)
dev.example.com {
    tls internal
    reverse_proxy localhost:3000
}

ACME 配置 #

使用不同 CA #

caddyfile
{
    # 全局配置 ACME CA
    acme_ca https://acme-v02.api.letsencrypt.org/directory
}

example.com {
    respond "Production CA"
}

使用测试 CA #

caddyfile
{
    # 使用 Let's Encrypt 测试环境
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

example.com {
    respond "Staging CA"
}

自定义 ACME 配置 #

caddyfile
example.com {
    tls {
        # 自定义 ACME 端点
        issuer acme {
            dir https://acme-v02.api.letsencrypt.org/directory
            email admin@example.com
        }
    }
    respond "Custom ACME"
}

ZeroSSL 证书 #

caddyfile
example.com {
    tls {
        issuer zerossl {
            eab_kid {env.ZEROSSL_KID}
            eab_hmac_key {env.ZEROSSL_HMAC_KEY}
        }
    }
    respond "ZeroSSL certificate"
}

证书故障排查 #

常见问题 #

bash
# 1. 检查端口是否开放
sudo netstat -tlnp | grep -E ':80|:443'

# 2. 检查域名解析
dig example.com +short

# 3. 检查防火墙
sudo ufw status

# 4. 检查证书状态
curl localhost:2019/certificates | jq

# 5. 查看日志
sudo journalctl -u caddy -f

验证证书 #

bash
# 检查证书详情
openssl s_client -connect example.com:443 -servername example.com

# 检查证书过期时间
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

# 验证证书链
openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt /path/to/cert.crt

常见错误解决 #

caddyfile
# 错误1:端口被占用
# 解决:检查并释放端口

# 错误2:域名未解析
# 解决:配置 DNS 解析

# 错误3:防火墙阻止
# 解决:开放端口
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# 错误4:证书获取频率限制
# 解决:使用测试 CA 或等待
{
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

完整示例 #

生产环境配置 #

caddyfile
{
    email admin@example.com
    acme_ca https://acme-v02.api.letsencrypt.org/directory
}

example.com {
    tls {
        protocols tls1.2 tls1.3
    }
    
    root * /var/www/html
    file_server
    encode gzip zstd
    
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
    }
    
    log {
        output file /var/log/caddy/access.log
    }
}

www.example.com {
    redir https://example.com{uri} permanent
}

通配符证书配置 #

caddyfile
{
    email admin@example.com
}

*.example.com, example.com {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }
    
    @api host api.example.com
    handle @api {
        reverse_proxy localhost:3000
    }
    
    @www host www.example.com
    handle @www {
        root * /var/www/html
        file_server
    }
    
    handle {
        respond "Default handler"
    }
}

双向 TLS 配置 #

caddyfile
api.example.com {
    tls {
        client_auth {
            mode require_and_verify
            trusted_ca_file /etc/ssl/ca.crt
        }
    }
    
    reverse_proxy localhost:3000 {
        header_up X-Client-Cert {tls_client_cert}
        header_up X-Client-CN {tls_client_subject}
    }
}

下一步 #

现在你已经掌握了 SSL/HTTPS 配置,接下来学习 缓存配置 了解如何优化网站性能!

最后更新:2026-03-28