我之前一直使用Cloudflare Tunnel免费版来反向代理Nextcloud容器。虽然Cloudflare Tunnel使用方便,但免费版有 100MB上传限制,这成为我使用Nextcloud时的主要障碍。
本文记录了我从Cloudflare Tunnel迁移至直接使用Traefik反向代理,并配置Let’s Encrypt TLS证书的完整过程,包括设置、遇到的问题和解决方案。
迁移原因
#Cloudflare Tunnel免费版的优点
#- ✅ 自动SSL/TLS终止
- ✅ 无需端口转发
- ✅ 支持动态IP
- ✅ 内置DDoS防护
Cloudflare Tunnel免费版的缺点
#- ❌ 100MB上传限制(主要问题)
- ❌ 带宽限制
- ❌ 有tunnel额外延迟
- ❌ 依赖Cloudflare基础设施
为什么选择Traefik + Let’s Encrypt
#- ✅ 无上传限制(可配置)
- ✅ 直接连接(更低延迟)
- ✅ 自动证书管理
- ✅ 完全控制路由
- ✅ 可处理多个服务
当前架构
# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| ┌────────────────────────────────────────────────────────────┐
│ Internet │
└────────────────────────────┬───────────────────────────────┘
│
┌──────────▼──────────┐
│ 路由器 │
│ 端口转发 │
│ 80, 443 → 服务器 │
└──────────┬───────────┘
│
┌──────────▼──────────┐
│ Traefik │
│ (TLS 终止) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Nextcloud │
│ (Docker) │
└─────────────────────┘
|
之前: 互联网 → Cloudflare Tunnel → Traefik → Nextcloud
现在: 互联网 → Traefik → Nextcloud
前置条件
#- Docker和docker-compose已安装
- Traefik容器正在运行
- 域名(如
nextcloud2.example.com) - Cloudflare账户且域名DNS由其管理
- 服务器有公网IP(或使用动态DNS)
步骤1: 移除Cloudflare Tunnel
#停止cloudflared容器
#1
2
| cd /path/to/cloudflared
docker compose down
|
更新cloudflared配置(可选)
#如果想保留cloudflared为其他服务,从config.yaml中移除Nextcloud条目:
1
2
3
4
5
6
7
8
9
10
11
12
| ingress:
# 删除以下行:
# - hostname: "cloud.example.com"
# service: https://traefik:443
# originRequest:
# noTLSVerify: true
# - hostname: "nextcloud2.example.com"
# service: https://traefik:443
# originRequest:
# noTLSVerify: true
# 保留其他条目...
- service: http_status:404
|
步骤2: 在Cloudflare更新DNS
#为每个要使用Traefik的域名:
- 进入 Cloudflare仪表板 → DNS
- 找到DNS记录(如
nextcloud2.example.com) - 从橙色云(代理)改为灰色云(仅DNS)
- 指向服务器的公网IP地址
重要: 为了让Let’s Encrypt DNS challenge正常工作,流量必须直接到达你的服务器,而不是通过Cloudflare代理。
步骤3: 配置Traefik的Let’s Encrypt DNS Challenge
#更新traefik.yml
#确保有ACME配置和Cloudflare DNS challenge:
1
2
3
4
5
6
7
8
9
10
| certificatesResolvers:
le:
acme:
email: [email protected]
storage: /letsencrypt/acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
|
设置Cloudflare API Token
#创建或更新traefik目录下的.env文件:
注意: DNS challenge需要Cloudflare API Token(不是全局API密钥),具有以下权限:
更新docker-compose.yml
#确保环境变量传递给Traefik:
1
2
3
4
| environment:
- CF_API_KEY=${CF_API_KEY}
- CF_API_EMAIL=${CF_API_EMAIL}
- TZ=${TZ:-UTC}
|
步骤4: 配置动态路由器带TLS
#在traefik/dynamic/目录下创建文件(如nextcloud2.yml):
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
| http:
routers:
nextcloud2-router-http:
rule: "Host(`nextcloud2.example.com`)"
entryPoints:
- web
middlewares:
- redirect-https
service: nextcloud2-service
nextcloud2-router-https:
rule: "Host(`nextcloud2.example.com`)"
entryPoints:
- websecure
tls:
certResolver: le # 重要:指定Let's Encrypt解析器
service: nextcloud2-service
services:
nextcloud2-service:
loadBalancer:
servers:
- url: "http://nextcloud:80" # Nextcloud容器名称
passHostHeader: true
middlewares:
redirect-https:
redirectScheme:
scheme: https
permanent: true
|
关键配置点
#需要两个路由器:
- HTTP路由器用于重定向(端口80)
- HTTPS路由器用于实际流量(端口443)
certResolver: le至关重要 - 没有它,Traefik使用内部自签名证书
passHostHeader: true - 保留原始Host头
步骤5: 重启Traefik
#1
2
| cd /path/to/traefik
docker compose up -d --force-recreate traefik
|
Traefik将:
- 监听新配置文件
- 通过DNS challenge请求Let’s Encrypt证书
- 创建
_acme-challenge.nextcloud2.example.com TXT记录 - 等待DNS传播
- 验证所有权
- 下载证书
步骤6: 验证设置
#检查证书是否已颁发
#1
2
3
4
5
| # 查看所有证书
cat /path/to/traefik/acme.json | python3 -m json.tool
# 检查特定域名
cat /path/to/traefik/acme.json | grep -A 5 'nextcloud2'
|
测试HTTPS连接
#1
2
3
4
5
6
7
8
| # 验证证书
echo | openssl s_client -connect nextcloud2.example.com:443 -servername nextcloud2.example.com 2>/dev/null | openssl x509 -noout -issuer -dates
# 验证域名匹配
echo | openssl s_client -connect nextcloud2.example.com:443 -servername nextcloud2.example.com 2>/dev/null | openssl x509 -noout -subject -text | grep -E "(Subject:|DNS:)"
# 测试连接
curl -I https://nextcloud2.example.com
|
添加更多域名
#要添加更多域名,只需创建更多配置文件:
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
32
33
| # 为另一个域名创建配置
cat > /path/to/traefik/dynamic/blog.yml <<'EOF'
http:
routers:
blog-router-http:
rule: "Host(`blog.example.com`)"
entryPoints:
- web
middlewares:
- redirect-https
service: blog-service
blog-router-https:
rule: "Host(`blog.example.com`)"
entryPoints:
- websecure
tls:
certResolver: le
service: blog-service
services:
blog-service:
loadBalancer:
servers:
- url: "http://blog:80"
passHostHeader: true
middlewares:
redirect-https:
redirectScheme:
scheme: https
permanent: true
EOF
|
Traefik每10秒自动重载配置,不需要重启。
常见问题及解决方案
#1. 证书显示为自签名
#问题: 浏览器显示"不安全"或自签名证书警告
解决方案: 在路由器配置中添加certResolver: le。没有它,Traefik使用内部证书。
2. DNS Challenge失败
#问题: 证书未颁发,日志中有错误
解决方案:
- 验证Cloudflare API token有正确权限
- 检查Traefik能否通过环境变量访问API token
- 确保DNS未被Cloudflare代理(灰色云)
- 查看日志:
docker logs traefik
3. 证书存在但不适用于域名
#问题: Let’s Encrypt证书存在,但不是你的域名
解决方案: Traefik是按需创建证书的。只需访问该域名,Traefik将自动请求证书。
4. HTTP到HTTPS重定向不工作
#问题: HTTP请求不重定向到HTTPS
解决方案: 确保你配置了两个路由器(HTTP和HTTPS)并带有重定向中间件。
5. Nextcloud显示"受信任域名错误"
#问题: Nextcloud不信任新域名
解决方案: 更新Nextcloud的受信任域名:
在nextcloud/docker-compose.yml中更新:
1
2
| environment:
- NEXTCLOUD_TRUSTED_DOMAINS=nextcloud2.example.com,cloud.example.com,previous-domain.com
|
或在Nextcloud配置中(config/config.php):
1
2
3
4
5
| 'trusted_domains' => [
'nextcloud2.example.com',
'cloud.example.com',
'previous-domain.com',
],
|
高级配置
#启用调试日志
#1
2
3
| # traefik.yml
log:
level: DEBUG
|
配置证书TTL
#1
2
3
4
5
6
7
8
9
| certificatesResolvers:
le:
acme:
email: [email protected]
storage: /letsencrypt/acme.json
dnsChallenge:
provider: cloudflare
caServer: "https://acme-v02.api.letsencrypt.org/directory" # 生产
# caServer: "https://acme-staging-v02.api.letsencrypt.org/directory" # 测试
|
多个域名对应单个服务
#1
2
3
4
5
6
7
8
9
| http:
routers:
multi-domain-router-https:
rule: "Host(`domain1.com`) || Host(`domain2.com`) || Host(`domain3.com`)"
entryPoints:
- websecure
tls:
certResolver: le
service: my-service
|
监控与维护
#检查证书过期
#1
2
3
4
5
6
7
8
| # 脚本检查所有证书
cat /path/to/traefik/acme.json | python3 <<'EOF'
import json, sys, datetime
data = json.load(sys.stdin)
for domain in data['le']['Certificates']:
cert = domain['certificate']
print(f"Domain: {domain['domain']['main']}")
EOF
|
手动证书续期
#Let’s Encrypt证书每90天自动续期。强制续期:
1
| docker compose exec traefik traefik renew-certs
|
备份acme.json
#1
| cp /path/to/traefik/acme.json /path/to/traefik/acme.json.backup-$(date +%Y%m%d)
|
性能对比
#| 指标 | Cloudflare Tunnel | Traefik + Let’s Encrypt |
|---|
| 上传限制 | 100MB ❌ | 无限制 ✅ |
| 延迟 | 较高(tunnel开销) | 较低(直接) ✅ |
| SSL证书 | 自动 ✅ | 自动 ✅ |
| 成本 | 免费(有限制) | 免费(无限制) ✅ |
| 配置 | 简单 | 中等 |
| DDoS防护 | 内置 ✅ | 需单独添加 |
从Cloudflare Tunnel迁移到Traefik + Let’s Encrypt是直截了当的,提供:
关键要点:
- 移除Cloudflare Tunnel for the domain
- 更新DNS为仅DNS(非代理)
- 配置Traefik with ACME + Cloudflare DNS challenge
- 在路由器配置中设置
certResolver: le - 重启Traefik请求证书
这个设置非常适合自托管服务,并且随着你添加更多域名而扩展。
更新日志
#