你把 URL 粘到 Slack,展开只有一个标题、没有图。或者第一次有图、之后再也不出。或者 X 有、LinkedIn 没有。Open Graph 是 2010 年的协议,但每个平台都在它之上叠了自己的规则:最小尺寸、爬取超时、支持的格式,以及一套服务器端缓存——一个错误的结果能被钉住长达一周。
最快的修复(覆盖约 70% 的情况): 确认页面里有一个绝对 HTTPS 的 og:image、并且它返回 HTTP 200,然后强制每个平台重新爬取。先跑这条:
curl -s "https://yoursite.com/article" | grep -oE 'og:image" content="[^"]+'
如果打印出了 URL 且以 https:// 开头,把它复制出来确认能加载:
curl -sI "https://yoursite.com/og.png" | head -1
如果这一行是 HTTP/2 200,说明标签没问题,问题几乎肯定是平台缓存陈旧——直接跳到 强制刷新每个平台的缓存。如果上面两条任意一条返回空或非 200,就按下面的原因逐条排查。
你属于哪一类?
| 现象 | 最可能的原因 | 对应小节 |
|---|---|---|
| 所有平台都没图 | 标签缺失、URL 是相对路径或 HTTP、图片非 200 | 原因 1、4 |
| 浏览器预览正常,分享时不出 | 缓存陈旧,或动态图爬取超时 | 原因 3、5 |
| X 显示小卡片,LinkedIn/FB 显示大图 | 缺 twitter:card 或图片太小 | 原因 2、6 |
| 昨天还好,发版后坏了 | 缓存仍是旧结果;图片 URL 或路径变了 | 原因 3 |
| 只有 Slack 不出 | Slack 图片代理缓存或格式不支持 | 原因 7 |
常见原因
按命中频率从高到低排列。
1. og:image URL 是相对路径或 HTTP
Open Graph 要求绝对 HTTPS URL。平台会拒绝 <meta property="og:image" content="/images/cover.png">,大多数也不会跟进纯 http:// 的 URL。
怎么判断:
curl -s "https://yoursite.com/article" | grep -oE 'og:image" content="[^"]+'
打印出的 URL 不以 https:// 开头,就是这条。
2. 图片太小或宽高比不对
每个平台都有自己的下限(截至 2026 年 6 月):
| 平台 | 最小 | 推荐 | 比例 |
|---|---|---|---|
X (summary_large_image) | 300x157 | 1200x628 | 1.91:1 |
200x200 | 1200x630 | 1.91:1 | |
400x400 | 1200x627 | 1.91:1 | |
| Slack | 200x200 | 1200x630 | 任意 |
一张 1200x630(1.91:1)的图能同时满足这四家。宽度低于 600px 时,X 会放弃大卡片、回退到 summary(无图),哪怕你声明了 summary_large_image。LinkedIn 对宽度低于 1200px 的图会渲染模糊或裁切。
怎么判断: 在浏览器里打开 og:image 的 URL 看像素尺寸,或者跑 curl -s URL | file - 确认它是真图片、不是返回了 HTML 错误页。
3. 平台缓存了旧结果(最常见的”昨天还好”情形)
X、Facebook、LinkedIn 都把爬取结果缓存在服务器端。如果页面在图片修好之前就上线过,平台就钉住了”无图”那条记录。截至 2026 年 6 月,缓存窗口大致是:Facebook 约 7 天(首次抓到的 URL 可能钉两周),LinkedIn 约 7 天,X 约 7 天,Slack 约 30 分钟。强制重爬是唯一能可靠跳过这个窗口的办法——见第 3 步。
怎么判断: 把页面放进 Facebook Sharing Debugger。如果还显示旧图(或”无图”),就是缓存陈旧。
4. 图片返回非 200
og:image 的 URL 返回 404、403(需要 auth)或 500,任何爬虫都拉不到。
怎么判断:
curl -sI "https://yoursite.com/og.png" | head -1
必须是 HTTP/2 200。跳转(301/302)有时能用、但不是每个平台都跟,最好直接吐出最终图片。同时确认 Content-Type 是 image/png、image/jpeg 或 image/gif——截至 2026 年 6 月,Facebook 文档对 og:image 仍只列 JPG、PNG、GIF,WebP/AVIF/SVG 在各平台表现都不稳:
curl -sI "https://yoursite.com/og.png" | grep -i content-type
5. 动态生成的图慢或超时
如果你现生成卡片(基于 Satori 的 @vercel/og,现已内置进 Next.js App Router 的 next/og),首次冷启动请求可能要几秒。一些爬虫等到约 2 秒就放弃、当作没图。
怎么判断:
curl -o /dev/null -s -w "%{time_total}\n" "https://yoursite.com/api/og?title=test"
如果热请求稳定高于约 2 秒,爬虫就会超时。修法是激进缓存,让只有第一次生成慢(第 5 步)。
6. twitter:card meta 缺失或类型错
要让 X 显示大图,必须同时有 og:image 和 <meta name="twitter:card" content="summary_large_image">。没有它,X 就用小卡片。
怎么判断:
curl -s "https://yoursite.com/article" | grep twitter:card
想要大预览就要看到 summary_large_image。(X 会回退用 Open Graph 标签里的标题、描述、图片,所以不必额外重复写 twitter:image——但 twitter:card 类型是必需的。)
7. 跨域 CORS / CSP / Slack 图片代理问题
如果图片放在另一个域名上、CORS 严格或 Content-Security-Policy 太紧,有些平台就拉不到。Slack 还会把图片走自己的图片代理(Slack-ImgProxy),它和元数据是分开缓存的——所以哪怕元数据刷新了,旧图也可能残留。
怎么判断: 把图片放在自己的原站(或一个宽松的 CDN)上,别用 CORS 受限的第三方主机。Slack 的情况见第 3 步的 ?v=2 技巧。
最短修复路径
第 1 步:查看源代码验证标签
curl -s "https://yoursite.com/article" | grep -E 'og:|twitter:'
至少要有:
<meta property="og:title" content="..." />
<meta property="og:description" content="..." />
<meta property="og:image" content="https://yoursite.com/og.png" />
<meta property="og:url" content="https://yoursite.com/article" />
<meta name="twitter:card" content="summary_large_image" />
og:image 必须是绝对 HTTPS。如果你的框架是在客户端注入标签(JS 渲染的 SPA),爬虫不跑 JS——标签必须出现在 curl 看到的原始 HTML 里。
第 2 步:验证图片 URL 返回 200
curl -sI "$(curl -s 'https://yoursite.com/article' | grep -oE 'og:image" content="[^"]+' | sed 's/.*content="//')"
要看到 200 和 Content-Type: image/png(或 jpeg/gif)。
第 3 步:强制刷新每个平台的缓存
官方 X validator cards-dev.twitter.com/validator 已停用(不再渲染预览),所以截至 2026 年 6 月,各平台的流程是:
- Facebook / Threads / WhatsApp: developers.facebook.com/tools/debug -> 输入 URL -> Scrape Again。如果预览没更新,多点 2-3 次;CDN 边缘节点可能滞后到 24 小时。
- LinkedIn: linkedin.com/post-inspector -> 输入 URL -> Inspect。这只刷新未来帖子的缓存;已经发出去的链接会一直显示旧预览,除非删掉重发。
- X (Twitter): 没有可用的官方 validator。把 URL 粘进 X 的发帖框(别真发),卡片会按当前标签实时渲染。第三方预览工具也能做静态检查。
- Slack: 没有公开刷新工具,缓存约 30 分钟。要强制重新抓取,加一个一次性的查询参数让 Slack 当成新 URL:粘
https://yoursite.com/article?v=2。删掉那条已展开的消息也会清掉它的缓存记录。
第 4 步:明确写尺寸和 alt 文本
声明尺寸能让平台不先抓图就渲染大卡片,也顺带避开原因 5 的超时:
<meta property="og:image" content="https://yoursite.com/og.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="图片的纯文字描述" />
第 5 步:动态 OG 图要激进缓存
如果你用 @vercel/og / next/og 生成图片,它在生产环境已经会设 cache-control: public, immutable, no-transform, max-age=31536000。如果是你自己拼响应,就设同样的头,让只有第一次爬取付出生成成本:
return new ImageResponse(content, {
width: 1200,
height: 630,
headers: {
'cache-control': 'public, immutable, no-transform, max-age=31536000',
},
});
长 max-age 意味着爬虫每次都命中已缓存、很快的响应。卡片内容变了就靠改 URL(内容 hash 或版本参数)来失效,而不是去降低 max-age。
第 6 步:确认确实修好了
别只信一个 validator——它们有时跟真实帖子渲染得不一样。修完后:
- 重跑 Facebook Sharing Debugger,确认新一次爬取显示了新图和
og:image:width/height。 - 把 URL 粘进 X 发帖框,确认大卡片能渲染。
- 跑 LinkedIn Post Inspector,确认预览。
- 在一个私密 Slack 频道分享一次 URL(或带
?v=2),确认图能展开。
四个都出图,就完成了。
常见问题
为什么图片在浏览器里能看到、分享时却不出? 浏览器是正常抓页面;社交平台另派一个爬虫 bot 抓取、然后缓存结果。常见原因是图片修好之前留下的陈旧缓存(强制重爬,第 3 步),或者动态图在首次冷启动时超时(缓存它,第 5 步)。
我把标签改对了,但还是显示旧图,多久才会更新? 不手动刷新的话,Facebook、LinkedIn、X 大约要 7 天,Slack 约 30 分钟(截至 2026 年 6 月)。用第 3 步里各平台的刷新工具跳过等待。注意 LinkedIn 的刷新只对新帖生效;已有帖子保留旧预览。
og:image 能用 WebP、AVIF 或 SVG 吗?
当作不支持。截至 2026 年 6 月 Facebook 文档只列 JPG、PNG、GIF,SVG/AVIF 在各平台渲染不一致。导出一张 1200x630 的 PNG 或 JPEG。
为什么 X 显示小卡片、LinkedIn 显示大图?
X 除了 og:image 还需要 <meta name="twitter:card" content="summary_large_image">。没有它 X 就用小的 summary 卡片。图片宽度低于 600px 时它也会降级到小卡片或无图。
我的 OG 标签在开发者工具里是对的,但 curl 看不到,为什么?
你的标签很可能是客户端 JavaScript 注入的。社交爬虫不执行 JS,所以标签必须在服务端渲染的 HTML 里。把它们改成服务端渲染或放进静态的 <head>。
预防建议
- OG 标签永远用绝对 HTTPS URL,图片放在自己的原站上。
- 永远写上
og:image:width和og:image:height。 - 默认用一张
1200x630的 PNG 或 JPEG。 - 模板改完后,至少在两个平台重爬一次(Facebook Debugger + LinkedIn Inspector)。
- 动态 OG 图用
max-age=31536000缓存,靠改 URL 来失效,而不是降低 max-age。