花墨的 CDN 被"薅干了"

最近花墨的 CDN 流量包消耗速度异常的快,于是我叫上 DeepSeek 老师进行分析检查。

技术 · 2026/09/29

最近没什么想写的博客,逛站强度很高,看了好多播客。

看到许多站长都分享了自己 CDN 被盗刷的经历,我其实还挺害怕的,毕竟我在阿里云充值了10元巨款_(:з」∠)_

结合前几天我 CDN 欠费导致网站停运的经历,我这两天时不时就在看我新买的 CDN 流量包余额。

结果我发现才不到两天,流量包就消耗了差不多 3.4 GB,明显就不太对。

于是我叫上 DeepSeek 老师一起分析这异常的 CDN 流量消耗,还真分析出来不少东西。

因此我对网站的 CDN 相关防护进行了加强,并顺带对花墨的各个细节再次进行了优化。

写这篇博客来分享一下我的步骤和措施。

静态资源

DeepSeek 的思路是先分析项目打包后的 HTML、JS/CSS、图片这些静态资源,看看能否找到浪费流量的过大文件。

虽然我的接口返回数据、图片等资源还有优化空间,但是远远达不到 2 天 3.4G 的流量消耗。

于是便进入下一步,获取CDN离线日志,阿里云 CDN 的离线日志里每条记录都带响应字节数。

离线日志

离线日志获取的入口:阿里云控制台 - CDN - 日志管理 - 离线日志 - 日志下载。

这里 DeepSeek 额外指出了,一定要同时下载网站主域名和 API 域名,因为我的图片绝大部分都在 API 目录下。

由于日志下载没有时间范围选择,只能按天下载,所以我分别下载了 2026-09-27 和 2026-09-28 两天、两个域名的全部日志。

QQ20260928-131015.png

将所有日志的 .gz 压缩包,放在项目中提前创建好的工作区中,由 DeepSeek 进行扫描分析。

结果发现了一批明显异常的图片请求 / 疑似爬虫抓取。

在 8 月份的时候,我将博客的绝大部分图片压缩并转化为了 webp 格式,而之前许多带水印的原图依旧在 CDN 中,你可以从下列表格看到这两天 CDN 流量的主要构成。

类别 流量 占比 说明
博客旧图 compressed-* 1422.76 MB 75.0% 4614 次请求,爬虫抓的
自托管字体 356.35 MB 18.3% 主要是一个 17.7MB 的 TTF
JS/CSS 65.20 MB 3.4% 正常
/img/ 全局头像 24.42 MB 1.3% 未压缩
CSS 22.45 MB 1.2% 正常
HTML 13.43 MB 0.7% 正常
点滴图 8.70 MB 0.5% ✅ WebP 生效
其他 ~4 MB 0.2%

DeepSeek 根据日志给我找出了几个很明显的异常点,列出来给大家看一看:

1. 大量空 Referer。 4614 次请求里有 4612 次 Referer 为空。虽然正常浏览器请求在某些情况下也可能不带 Referer,但结合后面的 UA、请求时间以及请求目标来看,这批流量明显更像是程序化抓取。

2. UA 每请求一换。 4614 次请求用了 4545 个不同的 User-Agent。真人访客的 UA 只有个位数。而且这些 UA 全是伪造的:

Mozilla/5.0 (Windows; U; Windows 98; Win 9x 4.90) AppleWebKit/53xx ...
Mozilla/5.0 (Windows; U; Windows NT 4.0) AppleWebKit/53xx ...
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/5340 (KHTML, like Gecko) Chrome/3x ...

AppleWebKit/5340 是假的(真实 Chrome 是 537.36),Windows 98、NT 4.0 更不用说。

3. 抓的是已经不用的旧地址。 你 8 月那次 WebP 改造把正文引用全部改写成了 webp-(我核对过:现在正文里 280 个 webp- 引用,0 个 compressed-)。但旧的 compressed-*.png 文件还留在磁盘上、nginx 还照常返回 200,所以爬虫拿着 8 月之前索引到的 URL 一直在拉。

代价有多大:

磁盘上实际大小
compressed-QQ_1778838561725....png(爬虫在抓的) 1,646,544 B
webp-QQ_1778838561725....webp(站点现在用的) 117,552 B

单张图片就能达到 14 倍。 我把爬虫抓过的 46 个旧文件全查了一遍——46 个全部都有对应的 webp 版,旧文件加起来 1422.76 MB,而它们的 webp 对应版合计只有 3.22 MB。

时间线也吻合:爬虫 27 号 09:00 开始(你网站刚恢复的那一刻),15:00 冲到 304 MB/小时 / 3362 请求,到现在还没停。

修复方案

结合 DeepSeek 的建议和我的想法,我进行了以下修复方案,用来防止被异常读取,顺便优化花墨的体积、节省流量。

先解决 CDN 异常流量

CDN 防盗链

在阿里云开始 CDN 防盗链,可以有效杜绝异常的图片读取。

白名单方案

将 Referer 白名单设为 *.flowersink.com,关掉"允许空 Referer",配置仅需一分钟,即可防止所有爬虫。

而缺点是:直接打开图片URL、RSS 阅读源、Typora 无法正常看到图,但这个代价可以接受。

具体做法:阿里云控制台 - CDN - 域名管理 - 选择具体域名 - 管理 - 访问控制 - Referer黑/白名单 - 修改配置

后来我发现白名单存在一个严重的问题:白名单只允许我的域名访问图片,其他网站将无法加载我的 logo。

黑名单方案

我不太想一个个去通知朋友修改友链头像地址,于是我更换了思路,设置了一个永远不可能匹配的Referer黑名单,如图所示。

QQ20260928-141534.png

这样只要不带 Referer 的访问都会 403。

规则由于无法留空,所以我填写的是never-match.invalid

.invalid 是 IANA 保留的顶级域(RFC 2606),永远不可能成为真实域名,所以任何真实请求的 Referer 都不会匹配它。效果就是"黑名单实际什么都不拦,只靠『禁止空 Referer』生效"。

注意:**按照花墨目前的域名结构,这套防盗链方案只能开在 API 域名上。**主站不能直接这么配置,否则“禁止空 Referer”可能会把直接访问主站的请求也拦掉。

黑名单方案依旧存在一个问题:能被"带伪造 Referer"的爬虫绕过。

但根因(那 1.4 GB 的旧图)已经解决。即使以后真的有爬虫绕过 Referer 防盗链,能够抓到的主要也是现在正在使用的压缩资源,流量成本也已经比之前低了很多。

如果以后真的出现了问题,那我就只能将所需要的图片放在主域名下,并采用白名单方案了

清理旧图和原图

首先 DeepSeek 分析并整理出了服务器中所有的孤儿文件,并将他们清理掉。

孤儿文件:没有被项目引用、当前也没有实际使用的文件。

其次对现有被引用的图片进行压缩,网站中存在许多超尺寸的图片,例如只需要15*15的logo 文件尺寸实际上 1300 * 1300,于是做了一次无风险尺寸压缩:

文件 源分辨率 最大显示 建议上限
粉毛猫猫头.jpeg 646KB 196px(me-card) 512px
再花趴趴.png 1300×975 14px(页脚头像) 128px
方长趴趴.png 1237×928 96px(thanks-avatar) 256px
方长.jpg 114KB 96px 256px
本地 web/再花方长贴贴趴趴.png 1899×1424 75px(返回顶部) 256px

为了防止恶意调用原图,我还删除了博客中所有“原图”相关设计,原图单张最高能达到 20 MB。

我的服务器网速很一般,原图加载起来非常吃力,体验也并不好,最初我放在服务器单纯是想用作一个备份而已,但似乎完全没必要。

顺便做了网站优化

字体压缩

DeepSeek 新增了一种切分法,根据博客的文字出现频率切分为多个字体文件,按需进行加载,具体成果如下:

字体 原来 现在
NotoSansSC 16.95 MB 14 片,每片 128–169 KB,页面只加载 1–3 片
NotoEmoji 1.90 MB 107.8 KB
Inter 0.83 MB 114.4 KB
Inter-Italic 0.86 MB 127.2 KB

浏览器实测数据:首页只加载 6 个字体文件 / 772KB(原来约 20MB)

接口全量问题

在花墨早期的时候,博客列表我是简单的获取了全量正文,limit 设置的甚至是 999。

这就会导致每次访问列表,即使页面上我只需要展示标题和摘要,也总是会获取每篇博客的正文(一篇正文通常上千字)

每次获取列表接口,响应体大小约为: 331 KB。

取消正文获取,仅获取除正文外的其他信息后,该流量降低至:2.6 KB

最终成果

等到第二天(29 号),我将 28 号的离线日志全部下载下来让 DeepSeek 进行分析,得到了清晰的改动前后数据对比。

28 号改动前后对比:请求数涨了 86%,流量降了 69%。

请求数 下行 小时均值 字体 图片
改动前 00–11 时 10,179 1,136.0 MB 94.7 MB/时 416.5 MB 615.5 MB
改动后 12–23 时 18,981 355.2 MB 29.6 MB/时 41.8 MB 145.8 MB

27 号和 28 号同一时段对比:请求数涨 57%、流量降 76%。

请求数 下行 小时均值 图片
27 号(改动前) 9,460 1,188.5 MB 198.1 MB/时 1,011.0 MB
28 号(改动后) 14,887 282.6 MB 47.1 MB/时 142.8 MB

按改动后的速率(约 0.7 GB/天)估算,100 GB 的流量包够用 140 天以上,也算是一个不错的结果了。

正在加载…