博客图片 CDN 配置指南(腾讯云)

发布于
博客图片 CDN 配置指南(腾讯云)

博客图片 CDN 配置指南(腾讯云)

上线日期:2026-09-30 | 服务商:腾讯云 CDN | 面向对象:站长本人(复习用) 本文已脱敏:源站 IP 一律写 <源站IP>,配置时自行代入。


一、为什么要配 CDN?目的是什么

配 CDN 之前,图片是怎么走的:

读者浏览器(全国各地)
   └─ 全部请求涌向源站服务器(单台轻量服务器)
        └─ nginx → blog-api 读出图片 → 返回

这条链路有三个问题:

  1. 慢:服务器只有一台、在单个机房。北京读者和广州读者访问速度完全取决于离机房远近,跨省延迟高,图片首屏加载慢。

  2. 扛不住:每张图都要源站 CPU + 带宽亲自出力。文章被分享到外站带来一波流量、或者遇到爬虫/恶意刷量,源站带宽被打满,连网页本身都会跟着卡死——图片把主站拖垮。

  3. 重复劳动:同一张图被 1000 个人看 1000 次,源站就傻傻地读 1000 次。明明内容一秒都没变过。

配 CDN 之后:

读者浏览器
   └─ 腾讯 CDN 边缘节点(全国分散部署,离读者最近的一个)
        ├─ 节点已有这张图 → 直接返回(速度极快,源站毫不知情)★90% 的情况
        └─ 节点没有 → 回源站取一次,存在节点上,下次就近服务

收益一句话总结:图片加载更快(就近取)、源站更安全(带宽被 CDN 挡住)、源站更省(绝大部分请求不再到达源站)。

为什么不缓存网页、只缓存图片:网页(文章列表/内容)是动态生成、随时可能改,缓存了会出现"发文章半天看不到"的问题;而图片一旦上传内容永远不变(文件名含日期/时间戳),是完美的缓存对象——这也是为什么单独拿一个 cdn. 子域名来托管图片。


二、原理:一个 CNAME 背后的调度

CDN 加速的本质是改 DNS 指向 + 换个更近的服务器替你回答请求:

  1. 你在腾讯云添加加速域名(如 cdn.mashangan.com),腾讯给你一个"CNAME 目标地址"(形如 xxxx.cdn.dnsv1.com)。

  2. 你在 DNSPod 把 cdn.mashangan.com 的解析从 A 记录(直接指向源站 IP)改成 CNAME 记录(指向腾讯的调度地址)。

  3. 之后读者解析 cdn.mashangan.com 时,腾讯的调度系统会根据读者的地理位置/运营商,返回离他最近的边缘节点 IP。

  4. 读者请求图片 → 边缘节点接住 → 有缓存直接返回,没缓存回源站取一次并缓存(默认 1 小时,由源站响应头 Cache-Control: max-age=3600 决定)。

如何确认请求真的到了腾讯边缘(复习用):浏览器 F12 → Network → 点开一张图片 → Response Headers 里找:

响应头

含义

X-Cache-Lookup: Cache Hit

命中边缘缓存(最理想)

Age: 17

这张图已在节点上放了 17 秒

X-NWS-LOG-UUID

腾讯云边缘节点才有的日志 ID(出现即说明到了腾讯)

Server: nginx/1.27.5

源站 nginx 透传过来的(说明是回源取的)


三、配置全流程(控制台逐步导航)

核心难点回顾:腾讯云产品众多,CDN 的配置入口藏得深。以下每一步都给出控制台页面路径。 控制台总入口:https://console.cloud.tencent.com/cdn (或在腾讯云控制台顶部搜索框输入"内容分发网络"/"CDN")

第 1 步:添加加速域名

  • 路径:CDN 控制台 → 左侧菜单「域名管理」→ 点「添加域名」

  • 直达链接:https://console.cloud.tencent.com/cdn/domains

  • 表单怎么填:

    • 加速域名:cdn.mashangan.com(子域名随便起,用 cdn 便于识别)

    • 加速区域:中国境内(要求域名已备案;若提示需要验证域名归属权,按提示到 DNSPod 加一条 TXT 记录即可)

    • 加速类型/业务类型:选 CDN 的网页小文件(以图片等小型静态资源为主的业务)

    • 源站信息:类型选「自有源」→ 填 <源站IP>,回源协议 HTTP

      • 为什么回源用 HTTP 不用 HTTPS:回源是"腾讯机房 → 你的服务器"的内段链路,走 HTTP 就行;对读者那一跳(读者 → 腾讯边缘)才是 HTTPS,由第 4 步的证书保证。这样源站压力最小。
    • 回源 HOST:默认(加速域名)即可——源站 nginx 已按该域名配好 vhost

  • 提交后回到「域名管理」列表,状态从"部署中"变为"已启动",同时列表里会显示分配给你的 CNAME 值(形如 cdn.mashangan.com.cdn.dnsv1.com),复制它。

第 2 步:改 DNS 解析(DNSPod 控制台)

  • 路径:DNSPod/云解析控制台 → 我的域名 → mashangan.com 行点「记录管理」

  • 直达链接:https://console.dnspod.cn (或腾讯云控制台搜"DNS 解析 DNSPod")

  • 操作:

    1. 点「添加记录」:主机记录 cdn、类型 CNAME、记录值粘贴第 1 步复制的调度地址、TTL 默认

    2. 如果 cdn 子域之前有过 A 记录(我们曾用"直指源站"应急),把那条 A 记录暂停(点禁用开关)——同一个主机记录 CNAME 和 A 不能同时生效

  • 验证解析已切换:本机命令行执行 nslookup cdn.mashangan.com,看到返回的地址是腾讯调度域名映射出的 IP 集群、且 alias 里带 cdn.dnsv1.com,即成功(刚改完有几分钟 TTL 缓存,急不得)

第 3 步:验证 HTTP 回源链路已通

此时先用 http:// 试访问一张真实图片(路径格式 http://cdn.mashangan.com/upload/年/月/文件名.png,可在文章页 F12 里随便复制一张图的地址把域名换掉):

  • 返回 200 且带 X-NWS-LOG-UUID 头 → 调度、缓存、回源全通 ✅

  • 返回 404 且 Body 是 NWS_SP 之类 → 该域名在腾讯侧被停用(见踩坑 #3)

  • 整个域 NXDOMAIN → DNS 记录没生效/被删(见踩坑 #3)

第 4 步:HTTPS 配置(★本次最大的坑在这里)

  • 路径:CDN 控制台 → 域名管理 → 点你的域名进详情页 → 左侧/顶部 Tab「HTTPS 配置」

  • 这个页面里有一排开关,逐个说明:

配置项

怎么填

说明

HTTPS 服务总开关

必须打开

带「付费」标签,按 HTTPS 请求次数计费(很便宜)。不开的话所有 HTTPS 请求一律返回 514 错误页——这是本次踩的最大坑,见踩坑 #1

证书配置

「配置证书」→ 选自有证书 → 把服务器上的证书 pem + key 两个文件内容粘贴/上传

服务器证书位置:源站 nginx 证书目录下的 cdn.mashangan.com.pem/.key(Let's Encrypt 签发,SFTP 下载即可)。也可用腾讯云免费证书(SSL 控制台申请后一键部署)

HTTP 2.0

开启

浏览器多路复用,图片并发加载更快

强制跳转

开启(HTTP → HTTPS)

有人用 http:// 访问时自动 302 到 https://

TLS 版本

1.2 / 1.3 开,1.0 / 1.1 关

老协议有安全漏洞

HSTS

暂不开(要开也从 1 天 max-age 起步)

单向门:浏览器记住后 max-age 内强制走 HTTPS 且无法回退。等 CDN 稳定运行一段时间再开,避免出问题时无法降级

OCSP 装订

可不开

证书状态查询优化,收益极小

第 5 步:HTTPS 全链路验证

  • 浏览器直接打开 https://cdn.mashangan.com/upload/... 某张图 → 200 即通

  • F12 看 Response Headers:X-Cache-Lookup / Age / X-NWS-LOG-UUID 齐全 ✅

  • http:// 访问同一地址 → 应 302 跳到 https(这是第 4 步"强制跳转"的预期表现,不是故障)

  • 项目内验证脚本(本地跑):python _md2html\_verify_prod_cdn.py(全站页面图片引用普查)、_probe_cdn_headers.py(边缘响应头)

第 6 步(强烈建议当天就做):防刷保险丝

都在域名详情页内:

配置

Tab 位置

填什么

为什么

用量封顶

「高级配置」Tab → 用量封顶

按天设流量阈值(如 10GB),超了自动停止服务并告警

防被盗刷出天价账单,个人博客必开

Referer 防盗链

「访问控制」Tab → Referer 防盗链

白名单 *.mashangan.com,必须同时勾选「允许空 Referer」

防其他网站直接嵌你的图。⚠️ 空 Referer 不勾会全站裂图:本站前端带 no-referrer 策略,图片请求不带 Referer,见踩坑 #5

节点缓存过期

「缓存配置」Tab

保持跟随源站(max-age=3600)即可

图片内容不变,理论上可以设更长,保守从 1 小时起


四、让站内图片真的走 CDN(应用侧切换)

CDN 域名通了只是"路修好了",站内 HTML 里的图片地址还指向源站,需要把引用切过去。本站的做法(2026-09-30 已落地,commit 38c5a89):

  • 新增 blog-web/utils/cdnize.ts 改写器,在三个数据出口统一把 https://cdn.mashangan.com/upload/ 相对路径(及历史遗留的旧绝对地址)改写为 https://cdn.mashangan.com/upload/...:

    1. useHaloApi.apiFetch —— 覆盖 SSR 首屏 + 客户端 SPA 导航两条取数路径

    2. server/api/site-config.get.ts —— 站点 logo

    3. server/routes/rss.xml.ts —— RSS 摘要

  • 不改数据库:改写发生在渲染之前,库里存的仍是相对路径;哪天想换 CDN 域名,改一处 env 即可

  • 开关:服务器上 halo-web 容器的环境变量 IMG_CDN_BASE: "https://cdn.mashangan.com"(compose 里是映射格式 KEY: value,注意缩进对齐,见踩坑 #6)

  • 回退:删掉该环境变量 → docker compose up -d --force-recreate --no-deps halo-web → 图片立即回到相对路径直连源站,1 分钟生效

  • 验证:python _md2html\_verify_prod_cdn.py,预期全站页面 CDN 引用数 > 0、裸 https://cdn.mashangan.com/upload/ 引用数为 0


五、踩坑实录(复习重点)

坑 #1:HTTPS 全部 514(Frequency Capped)——本次耗时最长的坑

  • 现象:CDN 域名 DNS 已切、HTTP 访问图片正常 200,但所有 https:// 请求(无论什么路径)一律返回 514 错误页。

  • 走了的弯路:先怀疑 DNS 缓存没生效(等 3 分钟,没用)→ 再怀疑证书部署延迟(等 5 分钟,没用)→ 最后是控制台截图对照官方文档才定位。

  • 根因:域名详情页「HTTPS 配置」里的 HTTPS 服务总开关没打开。官方说明原文就写着:"HTTPS 服务关闭时,HTTPS 请求将被拒绝访问,响应 514 状态码"。这个开关是付费增值项,默认关闭。

  • 教训:CDN 排障先看"总开关类"配置,别先怀疑玄学缓存。514 不是限流(名字里的 Frequency Capped 有误导性),就是 HTTPS 服务没开。

  • 知识点:HTTP 能通 + HTTPS 全 514 = 100% 是 HTTPS 开关/证书问题。

坑 #2:http:// 请求返回 302 不是故障

开了「强制跳转」后,HTTP 请求 302 → HTTPS 是预期行为。验证时别把 302 当异常;想测纯 HTTP 链路,去关强制跳转或直接看跳转后的最终响应。

坑 #3:CDN 域名"离奇失效"两种表现(上线前发生过)

  • 表现 A:NXDOMAIN——cdn 子域名解析彻底消失。原因:此前的 DNS CNAME 记录不知何时被删(半成品配置)。应急:DNSPod 加一条 A 记录直指源站 IP(源站 nginx 本就配好了该域名的证书和反代,可以独立服务),先恢复访问再慢慢查。

  • 表现 B:边缘返回 404 且 Body 带 NWS_SP——解析正常、能到腾讯节点,但腾讯说没这个域名的加速配置。疑似该加速域名在腾讯侧被停用/未生效。同样用 A 记录应急。

  • 教训:源站 nginx 一直留着 cdn 域名的 vhost + 证书,就是给应急用的;DNSPod 里那条禁用状态的应急 A 记录保留勿删,回退时启用它、暂停 CNAME 即可。

坑 #4:CDN 侧证书不会自动续期

  • 服务器上的 Let's Encrypt 证书由 acme.sh 自动续期,但上传到腾讯 CDN 的那份是静态拷贝,acme.sh 管不到。

  • 到期日 2026-12-28。到期前(建议 12-14 设日历提醒):确认服务器证书已续签 → SFTP 下载新 pem/key → CDN 控制台 → 域名详情 → HTTPS 配置 → 证书配置 → 更换证书。

  • 过期症状:浏览器访问图片报证书过期警告、证书告警邮件。别慌,换证书即恢复,不影响其他域名。

坑 #5:开防盗链必须放行"空 Referer"

本站前端 HTML 带 <meta name="referrer" content="no-referrer">(历史原因,防旧图床防盗链),图片请求一律不带 Referer。若开启 Referer 白名单却拒绝空 Referer,所有站内图片请求都会被 CDN 拒绝 → 全站裂图。白名单 + 允许空 Referer 两者缺一不可。

坑 #6:compose 插环境变量的格式

服务器 docker-compose.yml 的 environment: 是映射格式(KEY: "value",没有 - 前缀)。插入新变量必须用同样格式、缩进对齐;写成列表格式(- KEY=value)会导致 YAML 解析失败,compose 所有操作全挂。改完先 docker compose config 校验再 up(部署脚本 _deploy_haloweb_cdn.py 里已内置校验+自动回滚备份)。

坑 #7:验证手段备忘

  • 图片到底走没走 CDN:F12 看图片请求的 X-Cache-Lookup / X-NWS-LOG-UUID 响应头

  • 全站图片引用普查:python _md2html\_verify_prod_cdn.py

  • 边缘响应头细看:python _md2html\_probe_cdn_headers.py

  • HTTP→HTTPS 跳转验证:python _md2html\_probe_http_redirect.py

  • DNS 记录状态:python _md2html\_check_cdn_golive.py

  • 部署(含 env 插入+校验+回滚):python _md2html\_deploy_haloweb_cdn.py


六、费用参考

项

计费方式

量级

CDN 流量

按下行流量计费(约 0.2 元/GB 起,购流量包更省)

个人博客图片量很小,每月几元以内

HTTPS 请求数

按 HTTPS 请求次数极低单价计费

可忽略,但必须开(坑 #1)

防意外三件套:用量封顶(第六步)+ 控制台"费用中心"设告警阈值 + 流量包(可选)。


七、应急回退手册

故障场景

回退动作

生效时间

CDN 边缘异常(5xx/4xx 大面积)

DNSPod:启用应急 A 记录(直指源站)、暂停 CNAME

TTL 内,约 10 分钟

只想站内图片不走 CDN

服务器:删 halo-web 的 IMG_CDN_BASE env → docker compose up -d --force-recreate --no-deps halo-web

1 分钟

证书过期(坑 #4)

CDN 控制台换新证书,无需回退

即时

容器重建后图片 404/502

可能是部署窗口,先强刷;再查 docker kill -s HUP halo-nginx 重新解析 upstream

即时


八、参考链接