一次OG图片生成器的缓存事故:URL竟是罪魁祸首

OG图片生成器因CDN以URL为缓存键、Slack爬虫剥离查询字符串,导致40%预览变空白。
一位开发者构建的OG图片生成API在接入Slack链接展开管线后,约40%的预览图变成灰色空白。根源不在渲染器,而在CDN缓存:图片URL以查询字符串承载标题和描述,但Slack的unfurling bot在某些客户端会剥离查询字符串再请求图片,导致不同内容的请求塌缩为同一缓存键,第一个响应被缓存给后续所有请求。修复方法是将完整参数集哈希到URL路径中,使查询字符串可选,从而让缓存键与内容一致。文章指出,测试环境与真实调用环境的差异是主要盲区,开发者应用不同平台爬虫验证预览行为,并检查CDN实际看到的请求而非仅自己发送的URL。
一次OG图片生成器的缓存事故:URL竟是罪魁祸首
当40%的Slack预览变成灰块
一位开发者构建了一个OG图片生成API,接收标题和描述,渲染出1200x630的卡片。在本地测试中一切正常。但当他将其接入一个链接展开管线,让代理向Slack频道发布数十条链接时,突然有三分之一的预览图变成了灰色空白。
问题不在渲染器,而在前置的CDN。这是开发者在博客中分享的真实经历,原文标题直指核心:“我的OG图片生成器向40%的Slack预览返回了空白PNG——缓存键是URL,而不是标题”。

缓存键与内容不一致的根源
每个生成的图片URL看起来像 /og?title=...&desc=...。CDN基于完整URL进行缓存,这本身是正确的。但问题在于:Slack的链接展开机器人在某些客户端中会剥离查询字符串再请求图片。于是 ?title=Deploy+failed 和 ?title=Deploy+succeeded 都塌缩成了 /og/,第一个响应就被缓存给了后续所有请求。结果,一张“部署失败”的卡片被展示在满是“部署成功”链接的频道中。
这个bug揭示了生成式图片API的一个脆弱点:当URL的参数承载内容时,“URL即缓存键”的习惯需要被审计。据开发者描述,修复方法简单有效——将完整参数集哈希到路径本身(如 /og/a3f9c2….png),并使查询字符串变为可选。缓存键与内容从此一致。
OG图片(Open Graph Image)是Open Graph协议的一部分。该协议由Facebook于2010年提出,通过在网页HTML的<meta property="og:image">等标签中嵌入结构化元数据,让社交平台在分享链接时能自动抓取标题、描述和缩略图,生成富媒体预览卡片。OG图片通常被设计为1200×630像素,以适配大多数平台的首图展示比例。当链接被粘贴到Slack、Twitter、Facebook等平台时,平台的爬虫会访问该URL,解析OG标签并拉取图片进行预览。如果图片URL返回空白或错误内容,预览就会显示为灰块或占位符。理解OG协议的工作机制,有助于定位问题究竟出在元数据缺失、图片渲染失败还是缓存层返回了错误响应。
链接展开(Link Unfurling)是Slack等协作平台的一项功能:当用户粘贴URL时,平台后端会异步抓取该链接的内容并生成预览卡片,包括标题、描述和缩略图。这一过程由平台的爬虫或“unfurler bot”完成,而非用户浏览器。关键之处在于,不同客户端(桌面端、移动端、Web端)的unfurler实现可能存在差异,包括对URL的规范化处理——例如是否保留查询字符串、是否解码特殊字符、是否忽略片段标识符等。这意味着开发者构造的URL在到达图片服务之前,可能已经被平台侧改写。缓存系统看到的请求与开发者发出的请求因此可能不一致,这正是本案中查询字符串被剥离、导致缓存键塌缩的直接原因。
为什么这个问题容易被忽视?
测试环境与真实调用环境的差异是主要盲区。开发者在本地测试时,请求直接携带完整查询字符串,CDN行为符合预期。但Slack的unfurling bot在“某些客户端”中的行为并未被模拟。这并非孤例:许多链接预览服务、社交平台爬虫在处理URL时都有各自的洗牌逻辑,有的移除查询参数,有的忽略片段标识符,有的对路径大小写敏感。
另一方面,CDN的缓存层对开发者来说通常是“透明”的——你发送什么,它就缓存什么。但CDN看到的是经过客户端(或中间件)处理后的请求,而不是你最初构造的URL。这种视角差是缓存事故的温床。
CDN(内容分发网络)的缓存机制遵循“键值对”模型:缓存键通常是请求的完整URL(含查询字符串),值是响应的内容。当后续请求的缓存键与已有条目匹配时,CDN直接返回缓存值,不再回源。这一设计在URL参数不影响内容时是高效的,但当URL参数本身就是内容标识时,任何对URL的改写都会导致缓存键与内容错位。更隐蔽的是,CDN的缓存层对开发者通常不透明——开发者只能看到自己发送的请求,而CDN看到的是经过客户端、中间件、代理层层处理后的请求。两者之间的视角差,使得缓存事故往往在真实流量下才暴露,本地测试难以复现。
给构建者的可操作建议
如果你正在构建链接预览、unfurler或基于图片API的卡片生成器,这位开发者的教训值得记录:
- 检查CDN看到的内容,而不仅仅是你发送的内容。使用不同客户端(尤其是Slack、Twitter、Facebook等平台的爬虫)测试预览效果。
- 将内容哈希嵌入路径,而非完全依赖查询字符串。路径是更稳定的缓存标识。
- 使查询字符串可选,确保即使被剥离也能返回正确(或至少可区分)的内容。
- 监控预览失败率。如果发现某一类链接的空白预览比例异常,优先排查缓存层。
开发者已将工具打包在 x402.freeq.one,供感兴趣的人研究。正如他总结的:“空白卡片的bug教给我的HTTP缓存知识,比任何博客文章都多。”这个案例再次证明,分布式系统中的缓存正确性往往取决于最不起眼的细节——比如一个查询字符串的去留。
相关推荐

Hermes Agent零基础实战:自进化AI助手
Hermes Agent 零基础教程:GitHub 41.7K 星的自进化 AI Agent 怎么装怎么用。本文拆解其学习循环、三层记忆与 Skill 自改进机制,对比 Claude Code 和 OpenClaw 的差异,并附 WSL 安装实操步骤。

RAG面试核心:离线建库与在线检索全流程拆解
拆解RAG检索增强生成的完整工作流程:离线阶段文档加载、切割、向量化、入库,在线阶段问题改写、向量检索、重排、生成,并解析RAG面试核心考点与系统优化方向。

AI大模型零基础入门:原理与API调用实战指南
从AI发展脉络到大模型训练原理,再到Token、Temperature、Top-P等核心概念,本文结合实战课程讲解系统提示词、Function Call、RAG与记忆能力的实现逻辑,并给出四类API调用场景的落地路径,适合零基础开发者入门。