Caddy V2 完全指南 🚀

🌐 现代化的自动 HTTPS Web 服务器和反向代理 - 简单、安全、高效的Web服务解决方案
📋 目录
🎯 Caddy V2 简介
Caddy 2 是一款现代化的开源Web服务器,使用Go语言编写,以其简单性和自动HTTPS功能而闻名。
🌟 主要特性
- 🔒 自动 HTTPS:自动申请和续期 SSL 证书(Let’s Encrypt集成)
- ⚡ 简单配置:简洁易懂的 Caddyfile 语法,JSON配置选项
- 🚀 高性能:基于 Go 语言开发,内存占用低,并发能力强
- 🔧 模块化:支持丰富的扩展功能和中间件
- 📦 零依赖:单一二进制文件,无需额外运行时
- 🌐 HTTP/3 支持:原生支持HTTP/3 (QUIC)协议
🆚 版本区别
- Caddy V1:旧版本,配置语法不同,已停止维护
- Caddy V2:新版本,完全重写,配置更灵活,本教程适用
🏆 适用场景
- 个人网站:简单配置,自动HTTPS
- API 网关:灵活的反向代理和负载均衡
- 静态网站:高效的文件服务
- 微服务架构:服务发现和动态配置
- 开发环境:快速搭建本地HTTPS环境
🐳 Docker 安装
📦 基础安装命令
1 2 3 4 5 6
| docker run -d --name=CaddyV2 \ --net=host \ -v /path/to/caddy/data:/data \ -v /path/to/caddy/config:/config \ -v /path/to/Caddyfile:/etc/caddy/Caddyfile \ caddy:latest
|
🎯 推荐安装配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #!/bin/bash
CADDY_NAME="CaddyV2" DATA_DIR="/docker/caddy/data" CONFIG_DIR="/docker/caddy/config" CADDYFILE="/docker/caddy/Caddyfile" WWW_DIR="/www"
mkdir -p ${DATA_DIR} ${CONFIG_DIR} ${WWW_DIR} touch ${CADDYFILE}
docker run -d --name=${CADDY_NAME} \ --restart=unless-stopped \ --net=host \ -v ${DATA_DIR}:/data \ -v ${CONFIG_DIR}:/config \ -v ${CADDYFILE}:/etc/caddy/Caddyfile \ -v ${WWW_DIR}:/www \ caddy:latest
echo "✅ Caddy V2 安装完成!" echo "📁 数据目录: ${DATA_DIR}" echo "📁 配置目录: ${CONFIG_DIR}" echo "📄 配置文件: ${CADDYFILE}" echo "🌐 网站目录: ${WWW_DIR}"
|
🔧 端口映射方式(替代 host 网络)
1 2 3 4 5 6 7 8
| docker run -d --name=CaddyV2 \ -p 80:80 -p 443:443 -p 443:443/udp \ -p 2019:2019 \ -v /docker/caddy/data:/data \ -v /docker/caddy/config:/config \ -v /docker/caddy/Caddyfile:/etc/caddy/Caddyfile \ -v /www:/www \ caddy:latest
|
🐳 Docker Compose 部署
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| version: '3.7'
services: caddy: image: caddy:latest container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" volumes: - ./data:/data - ./config:/config - ./Caddyfile:/etc/caddy/Caddyfile - /www:/www networks: - caddy-network
networks: caddy-network: driver: bridge
|
📝 配置文件详解
Caddy 2 支持两种配置格式:Caddyfile(人类可读)和JSON(机器可读)。
🎨 主配置文件结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { http_port 80 https_port 443 order reverse_proxy before file_server auto_https disable_redirects log { output file /data/access.log { roll_size 100mb roll_keep 10 } format json } }
import /etc/caddy/conf.d/*.conf
|
📁 配置文件组织建议
1 2 3 4 5 6 7
| /etc/caddy/ ├── Caddyfile └── conf.d/ ├── proxy.conf ├── static.conf ├── api.conf └── security.conf
|
🔄 动态配置(API驱动)
Caddy 2 提供了管理API,支持动态配置更新:
1 2 3 4 5 6 7 8 9
| { admin localhost:2019 }
curl -X POST "http://localhost:2019/load" \ -H "Content-Type: text/caddyfile" \ --data-binary @Caddyfile
|
🔁 反向代理配置
🎯 基础反向代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| hass.example.com { reverse_proxy localhost:8123 }
api.example.com { reverse_proxy https://backend-api:3000 }
app.example.com { reverse_proxy /api/* localhost:3000 reverse_proxy /* localhost:8080 }
|
⚙️ 高级代理配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| service.example.com { reverse_proxy { to server1:8080 server2:8080 server3:8080 lb_policy round_robin health_uri /health health_interval 30s health_timeout 5s fail_duration 30s max_fails 3 } header_up X-Real-IP {remote_host} header_up X-Forwarded-Proto {scheme} header_down Strict-Transport-Security "max-age=31536000;" reverse_proxy { to backend:8080 transport http { dial_timeout 10s tls_timeout 10s response_header_timeout 30s } } }
|
🔧 WebSocket 代理
1 2 3 4 5 6 7 8 9 10 11 12
| ws.example.com { reverse_proxy localhost:3000 { header_up Connection {>Connection} header_up Upgrade {>Upgrade} transport http { keepalive off } } }
|
🔄 服务发现集成
1 2 3 4 5 6 7 8 9 10 11 12
| service.example.com { reverse_proxy { to docker-swarm:service-name:8080 } }
|
📁 Web 服务配置
🎯 静态文件服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| www.example.com { root * /www/example file_server encode gzip zstd file_server browse { hide .gitignore .htaccess } handle_errors { @404 { expression {http.error.status_code} == 404 } rewrite @404 /404.html file_server } }
|
🛡️ 安全增强配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| static.example.com { root * /www/static file_server header { X-Content-Type-Options nosniff X-Frame-Options DENY X-XSS-Protection "1; mode=block" Referrer-Policy "no-referrer-when-downgrade" Permissions-Policy "geolocation=(), microphone=(), camera=()" } header Cache-Control "public, max-age=3600" header Content-Type "{http.response.header.Content-Type}" header -Server }
|
📄 单页面应用(SPA)支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spa.example.com { root * /www/spa file_server try_files {path} {path}/ /index.html @clientRoute { not file path_regexp .*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ } rewrite @clientRoute /index.html }
|
📊 日志记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| blog.example.com { root * /www/blog file_server log { output file /data/access-blog.log { roll_size 100mb roll_keep 5 roll_local_time } format json { time_format "iso8601" time_local } } }
|
🔒 HTTPS 配置
🎯 自动 HTTPS(默认)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
example.com { reverse_proxy localhost:3000 }
*.example.com { tls { dns cloudflare {env.CLOUDFLARE_API_TOKEN} } reverse_proxy localhost:3000 }
|
📝 手动指定证书
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| https://example.com:444 { tls /path/to/cert.pem /path/to/key.pem reverse_proxy localhost:3000 }
example.com { tls { ca https://acme-staging-v02.api.letsencrypt.org/directory dns cloudflare {env.CLOUDFLARE_API_TOKEN} } reverse_proxy localhost:3000 }
|
🌐 HTTP 重定向到 HTTPS
1 2 3 4 5 6 7 8 9 10 11 12 13
| http://example.com { redir https://{host}{uri} permanent }
https://example.com { reverse_proxy localhost:3000 }
example.com { reverse_proxy localhost:3000 }
|
🚫 纯 HTTP 服务
1 2 3 4 5 6 7
| http://example.com:8080 { root * /www/http file_server tls off }
|
🔐 客户端证书认证
1 2 3 4 5 6 7 8 9 10
| secure.example.com { tls /path/to/cert.pem /path/to/key.pem { client_auth { mode require_and_verify trusted_ca_cert_file /path/to/client-ca.crt } } reverse_proxy localhost:3000 }
|
⚙️ 高级配置
🔐 身份验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| admin.example.com { basicauth /admin/* { admin $2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx user2 $2y$10$yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy } jwt { primary yes allow sub user1 user2 secret {env.JWT_SECRET} } reverse_proxy localhost:3000 }
|
📊 高级日志配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| { log { output file /data/access.log { roll_size 1gb roll_keep 10 roll_local_time roll_uncompressed } format json { time_format "iso8601" time_local } output file /data/error.log { roll_size 50mb roll_keep 5 roll_local_time } level ERROR } }
|
⚡ 性能优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| { buffers { read 4096 write 4096 } timeouts { read 30s write 30s idle 2m graceful_shutdown 15s } encode { gzip { level 6 min_length 256 } zstd } file_server { cache { max_age 1h } } }
|
🔄 重写和重定向规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| example.com { rewrite /old/path /new/path rewrite /user/(\w+) /profile?name={1} @mobile { header User-Agent *mobile* } redir @mobile /mobile{uri} redir https://new.example.com{uri} permanent @withToken { query token=* } redir @withToken /auth{uri} }
|
🧩 中间件和插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| example.com { header Access-Control-Allow-Origin * header Access-Control-Allow-Methods "GET, POST, OPTIONS" header Access-Control-Allow-Headers "Content-Type, Authorization" rate_limit { zone rate_limit_zone 10 1s key {remote_host} } @whitelist { remote_ip 192.168.1.0/24 10.0.0.0/8 } respond @whitelist 403 reverse_proxy localhost:3000 }
|
🔧 故障排除
🐛 常见问题解决
1. 端口被占用
1 2 3 4 5 6 7 8 9 10 11 12
| sudo lsof -i :80 sudo lsof -i :443
{ http_port 8080 https_port 8443 }
sudo kill -9 $(sudo lsof -t -i:80)
|
2. 证书申请失败
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| dig example.com nslookup example.com
sudo ufw status sudo firewall-cmd --list-all
ls -la /www/.well-known/acme-challenge/
{ acme_ca https://acme-staging-v02.api.letsencrypt.org/directory }
|
3. 权限问题
1 2 3 4 5 6 7 8 9 10
| ls -la /path/to/www
sudo chown -R www-data:www-data /path/to/www sudo chmod -R 755 /path/to/www
sudo sestatus sudo aa-status
|
📝 日志查看和调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| docker logs CaddyV2 docker logs -f CaddyV2
tail -f /docker/caddy/data/access.log
docker exec CaddyV2 cat /data/caddy.log
{ debug }
docker run -e CADDY_DEBUG=true ... caddy:latest
|
🔍 配置验证和测试
1 2 3 4 5 6 7 8 9 10 11
| docker exec CaddyV2 caddy validate --config /etc/caddy/Caddyfile
docker exec CaddyV2 caddy reload --config /etc/caddy/Caddyfile
docker exec CaddyV2 caddy adapt --config /etc/caddy/Caddyfile
curl http://localhost:2019/config/ | jq .
|
🚀 性能监控和优化
1 2 3 4 5 6 7 8 9 10 11
| docker stats CaddyV2
docker exec CaddyV2 caddy metrics
watch -n 1 'docker stats CaddyV2 --no-stream'
ab -n 1000 -c 100 https://example.com/
|
🔄 维护和更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| docker pull caddy:latest docker stop CaddyV2 docker rm CaddyV2
tar -czvf caddy-backup-$(date +%Y%m%d).tar.gz /docker/caddy/
tar -xzvf caddy-backup-20231201.tar.gz -C /
docker exec CaddyV2 caddy cleanup
|
🎯 提示:Caddy V2 的配置非常灵活,建议从简单配置开始,逐步添加复杂功能。定期备份配置文件和证书数据。
📚 推荐资源:
🔧 维护命令:
1 2 3 4 5 6 7 8 9 10 11
| docker exec CaddyV2 caddy version
docker exec CaddyV2 caddy list-modules
docker exec CaddyV2 caddy validate --config /etc/caddy/Caddyfile
docker exec CaddyV2 caddy reload --force
|
💡 最佳实践:
- 使用Docker Compose管理Caddy容器
- 将配置和数据目录映射到宿主机
- 定期备份证书和配置
- 使用环境变量管理敏感信息
- 启用适当的日志记录和监控
- 定期更新Caddy到最新版本
🌐 社区支持:
希望本指南能帮助您顺利使用Caddy V2搭建和管理Web服务!🚀