Skip to content

挂 Cloudflare CDN ​

一句话

把图片和静态资源交给 Cloudflare 的全球节点发,你的服务器只在第一次被回源。 免费,代码一行不用改。


什么时候需要挂 ​

不是一上线就要挂。 先看几个数字再决定:

bash
# 看服务器闲不闲(三个数字都小于 CPU 核数就是闲着)
uptime
bash
# 看这段时间总共发出去多少流量
awk '{s+=$10} END{printf "%.1f MB\n", s/1048576}' /opt/mall/logs/nginx-access.log

别看错日志文件

HTTPS 的访问日志在 /opt/mall/logs/nginx-access.log(站点块里显式配的)。 默认的 /var/log/nginx/access.log 只剩 80 端口那点跳转流量 —— 查错文件会得出「几乎没人访问」这种看着有证据的错误结论。

出现下面任何一条,就该挂了:

  • 图片打开明显变慢,尤其是海外访客
  • 服务器带宽跑满,或者服务商开始按流量另外收费
  • uptime 的负载持续接近 CPU 核数
  • 你打算投广告,预计流量会涨一个数量级

挂 CDN ≠ 换对象存储

这两件事经常被混为一谈:

  • CDN:把你现有的图片缓存到全球节点。免费,改 DNS 就行 ← 先做这个
  • 对象存储(R2 / S3):把图片搬到别人的服务器上存。要迁移文件、改配置

先挂 CDN。 挂完之后源站的图片流量会趋近于零,多数人到此为止就够了。


⚠️ 开始之前:证书那件事必须先处理 ​

这是整篇文档最重要的一段,跳过它三个月后会出大事。

会发生什么 ​

现在的 HTTPS 证书是 Let's Encrypt 自动续期的,续期时 Let's Encrypt 要来访问 http://你的域名/.well-known/acme-challenge/xxx 验证域名归属(叫 HTTP-01)。

挂上 Cloudflare(橙云)之后,这个请求会先到 Cloudflare。能不能正确转到你的服务器, 取决于 Cloudflare 后台一个叫「Always Use HTTPS」的开关 —— 而那是很多人挂 CDN 时 顺手就打开的。

为什么后果特别严重 ​

三件事叠在一起

  1. 续期失败是静默的 —— 不报错、不发邮件。 Let's Encrypt 从 2025 年 6 月起不再发到期提醒邮件,没有任何人会通知你。
  2. 证书有效期 90 天 —— 出问题到发作隔着三个月,那时候早忘了动过什么。
  3. 我们的站点发 HSTS —— 证书一过期,浏览器直接拒绝连接, 不是"提示不安全但能点继续",是彻底打不开。而且用户那边的 HSTS 缓存 要等很久才过期。

解法:改用 DNS-01 验证 ​

DNS-01 是通过在 DNS 里加一条 TXT 记录来验证域名归属,完全不经过 HTTP, 所以和橙云开不开、Always Use HTTPS 开不开都没有关系。

必须在点橙云之前做完这一步。


第 1 步:换成 DNS-01 证书 ​

① 在 Cloudflare 建一个 API 令牌 ​

  1. 登录 Cloudflare → 右上角头像 → My Profile
  2. 左侧 API Tokens → Create Token
  3. 找到 Edit zone DNS 模板 → Use template
  4. 权限保持 Zone / DNS / Edit
  5. Zone Resources 选 Include / Specific zone / 你的域名
  6. Continue to summary → Create Token
  7. 把令牌复制下来 —— 页面关掉就再也看不到了

别用 Global API Key

那个 key 能操作你 Cloudflare 账号下的所有东西(包括删除域名)。 上面这个令牌只能改这一个域名的 DNS 记录,泄露了损失也有限。

② 在服务器上装插件并换证书 ​

bash
ssh root@你的服务器IP
bash
apt update && apt install -y python3-certbot-dns-cloudflare

把令牌写进文件(<你的令牌> 换成刚才复制的那串):

bash
printf 'dns_cloudflare_api_token = <你的令牌>\n' > /root/.cloudflare.ini && chmod 600 /root/.cloudflare.ini

chmod 600 不能省

这个文件里是能改你 DNS 的凭据。不限权限的话,服务器上任何一个进程都读得到。

重新签发证书(jdz.io 换成你的域名):

bash
certbot certonly --dns-cloudflare --dns-cloudflare-credentials /root/.cloudflare.ini --dns-cloudflare-propagation-seconds 30 -d jdz.io -d www.jdz.io --cert-name jdz.io --force-renewal

几个参数为什么这么写

  • --cert-name jdz.io —— 必须和原来的证书同名,否则会生成 jdz.io-0001 这样的新目录,而 nginx 还指着旧路径
  • -d 要把 www 也带上,和原来保持一致(证书不覆盖 www 的话, 访问 www 会直接握手失败)
  • --dns-cloudflare-propagation-seconds 30 —— 等 DNS 记录生效。默认 10 秒有时不够

③ 验证续期真的能跑(这一步别跳过) ​

bash
certbot renew --dry-run

看到 Congratulations, all simulated renewals succeeded 才算成功。

这是唯一能提前发现续期坏掉的办法

它模拟一次完整的续期流程。不跑这一步的话,你要等三个月后站点打不开才知道。

顺便确认自动续期的定时器还在:

bash
systemctl is-enabled certbot.timer && systemctl is-active certbot.timer

两行都应该输出 enabled / active。


第 2 步:确认 Cloudflare 的 SSL 模式 ​

Cloudflare 后台 → 选中你的域名 → SSL/TLS → Overview

必须选 Full (strict)。

模式会发生什么
Off不加密,绝对不能用
FlexibleCloudflare 用 http 回源,而我们的 80 端口是 301 跳 https → 无限重定向循环,站点打不开
Full回源用 https 但不验证证书 —— 能用,但中间人可以冒充你的源站
Full (strict) ⭐回源用 https 且验证证书。我们有真证书,就该用它

第 3 步:点橙云 ​

Cloudflare 后台 → DNS → Records

找到这两条,把灰色云朵点成橙色:

类型名称代理状态
A你的域名(或 @)🟠 Proxied
Awww🟠 Proxied

只点 A 记录

MX(邮件)、TXT(SPF / DKIM / DMARC 那些)保持灰色,它们本来就不该走代理。 点橙了会让邮件收发出问题。

改完立刻生效,不用等。


第 4 步:验证(五项,逐个跑) ​

① 域名解析到 Cloudflare 了吗 ​

bash
curl -sI https://你的域名/ | grep -i "^server:"

出现 server: cloudflare 就对了。

② 图片走缓存了吗 —— 最关键的一项 ​

先在浏览器打开任意商品页,右键一张图「复制图片地址」,然后:

bash
curl -sI 那个图片地址 | grep -i "cf-cache-status"

连着跑两次:

第一次第二次结论
MISSHIT✅ 正常。第一次回源,之后由边缘节点发
MISSMISS❌ 没缓存住,往下看「常见问题」
DYNAMICDYNAMIC❌ Cloudflare 认为这不是静态资源

③ 站点功能还正常吗 ​

打开你的商城,走一遍:首页 → 商品详情 → 加购 → 结算页。 再登录一次后台,传一张图(上传走的是同一条 /files/ 路径)。

④ 限流还认得出真实访客吗 ​

这一项已经提前配好了

本系统的 nginx 配置里已经写好了 Cloudflare 的真实 IP 还原 (set_real_ip_from + real_ip_header CF-Connecting-IP),你不需要做任何事。

没配的话后果是:全站访客被算成 Cloudflare 的几个边缘 IP, 限流变成所有人共用一个额度,访客统计也直接失真。

验证方法:登录一次后台,然后在服务器上看:

bash
tail -3 /opt/mall/logs/nginx-access.log | awk '{print $1}'

显示的应该是真实访客 IP,不是 172.x / 104.x 这类 Cloudflare 的地址。

⑤ 部署脚本还能正常跑吗 ​

bash
python deploy.py check

会看到一条关于 DNS 的警告,属正常

挂橙云后域名解析到 Cloudflare 的 IP,不再等于你服务器的 IP。 脚本会提示但不会中止 —— 当初就是这么设计的,因为那可能是你有意为之。


常见问题 ​

图片一直是 MISS,缓存不住 ​

先确认响应头里有长缓存:

bash
curl -sI 图片地址 | grep -i cache-control

应该是 public, max-age=31536000, immutable。本系统默认就是这个, 如果不是,说明 nginx 配置被改过或者没重新渲染:

bash
python deploy.py nginx

改了 nginx 一定要用 deploy.py nginx

deploy.py update 不会重写 nginx 配置,只有全量 deploy 和这条命令会。 改完模板跑 update 等于什么都没做,而且没有任何提示。

站点打不开,或者一直在跳转 ​

十有八九是 SSL 模式选成了 Flexible。改成 Full (strict)。

后台改了内容,前台不更新 ​

CDN 把旧内容缓存住了。Cloudflare 后台 → Caching → Configuration → Purge Everything。

调试期间可以开 Development Mode(Caching 页面右侧),它会临时绕过缓存 3 小时。

上传大文件失败 ​

Cloudflare 免费版的请求体上限是 100 MB

而本系统的交付文件上限默认也是 100 MB —— 正好卡在边界上。

超过 90 MB 的文件建议:

  • 用外部链接(虚拟商品的交付文件支持粘网盘地址),或者
  • 联系我们把上限调低

邮件收不到了 ​

检查 DNS 里的 MX 和 TXT 记录有没有被误点成橙色。它们必须是灰色(DNS only)。


怎么回滚 ​

随时可以退回去,30 秒

Cloudflare 后台 → DNS → Records → 把橙色云朵点回灰色。

立刻生效,流量恢复直连你的服务器。

换成 DNS-01 的证书不用回滚 —— 它在挂不挂 CDN 的情况下都能正常续期, 留着只有好处。


之后还想再省的话 ​

挂完 CDN 之后,源站的图片流量应该已经趋近于零。多数人到此为止就够了。

如果将来图片总量涨到几十 GB、或者你想让上传也更快,下一步是把图片从自己的服务器 搬到对象存储。推荐 Cloudflare R2:

  • 出网流量零费用(图片业务的成本大头永远是流量,不是存储)
  • S3 兼容,本系统换过去只改 .env 里的 S3_* 四个值,代码一行不动
  • 和 CDN 同一家,不用跨服务商调试

别急着做

搬到对象存储要迁移文件、改配置、验证每一张老图还能打开。 没到那个量级就是给自己找活干。

这份文档只讲后台怎么用,不含任何真实密钥