分享到 X/Twitter 不显示卡片图

og:image 都设好了,分享到 X/Twitter 还是没图。6 个真实原因、怎么判断你属于哪种、每种的最快修法。

你把文章 URL 粘进 X(原 Twitter)的推文输入框,预期看到带大图的卡片,结果要么是干巴巴的链接,要么只有标题没图,要么显示的还是两周前已经换掉的旧图。同样的 URL 在 LinkedIn、Slack 都好好的。这几乎从来不是「X 又坏了」。

最快的修法:确认页面服务端渲染出来的 HTML 里有 <meta name="twitter:card" content="summary_large_image">,加上一个绝对的 https:// og:image,然后用 curl -A "Twitterbot/1.0" -I <图片URL> 确认爬虫能抓到、返回 200。这能解决绝大多数情况。如果 meta 已经是对的、却还在显示旧图,那就是 X 的 7 天卡片缓存——推这条 URL 时在末尾加 ?v=2 强制重抓即可。

截至 2026 年 6 月,X 会把 Open Graph 当 fallback 来读,所以对大多数站点你不需要单独的 twitter:image——但你至少要有 twitter:card,而且这些标签必须出现在服务端渲染<head> 里,因为 X 爬虫不执行 JavaScript。

5 秒判断你是哪种情况

  • 卡片有标题和描述但没图 -> twitter:image / og:image 抓不到或图太小
  • 完全没有卡片,只是裸链接 -> 整个 twitter:card meta 缺失,或 content 值不被识别,或标签是被客户端 JavaScript 注入的
  • 换了图后还是显示旧图 -> X 卡片缓存(7 天)
  • Facebook / LinkedIn 都正常,只有 X 不行 -> 图用了相对路径、twitter:card 缺失,或 robots.txt 挡了 Twitterbot
  • 新 URL 都正常,只有某一条不行 -> 这条 URL 的图返回非 200(签名 URL 过期、文件已删)
  • 整站都没卡片,页面是 React/Vue/SPA -> 标签是页面加载后才加上的,爬虫根本看不到

常见原因(按命中率排序)

1. 缺 twitter:card 声明

没有 <meta name="twitter:card">,X 根本不知道要渲染成哪种形状的卡片,结果要么回退到小型 summary,要么完全不渲染。这是「首次分享没卡片」最常见的原因。

怎么判断:对页面 view-source(不是 DevTools 的 Elements 面板——那看到的是 JS 跑完之后的 DOM),搜 twitter:card。原始 HTML 里没有就是它。

修复:至少输出

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="文章标题" />
<meta name="twitter:description" content="一句话摘要" />
<meta name="twitter:image" content="https://yourdomain.com/og/article.png" />

summary_large_image 是大多数内容站想要的 1.91:1 宽卡片;summary 是带小方图的紧凑卡。

2. twitter:image 用了相对路径

X 爬虫不会解析相对 URL。content="/og/foo.png" 直接被当字符串拿去 fetch,自然失败。

<!-- 错:相对路径 -->
<meta name="twitter:image" content="/og/article.png" />

<!-- 对:绝对 https URL -->
<meta name="twitter:image" content="https://yourdomain.com/og/article.png" />

怎么判断:view-source 看 content 属性,/./ 开头都是错的。

3. 图不符合 X 的尺寸/格式要求

X 对 summary_large_image 有硬性要求(截至 2026 年 6 月):

字段规则
格式PNG / JPEG / WebP(动图 GIF 可,但只取第一帧)
最小尺寸300 x 157 px
最大尺寸4096 x 4096 px
最大文件大小5 MB
宽高比1.91:1(偏离会被居中裁切)
协议只接受 HTTPS

200x200 的 favicon、6MB 的大图、http:// URL 都会静默失败——卡片可能还会渲染,但图位是空的。

怎么判断

curl -A "Twitterbot/1.0" -I "https://yourdomain.com/og/article.png"
# 期望:HTTP/2 200, content-type: image/png, content-length 小于 5000000
identify "https://yourdomain.com/og/article.png"   # ImageMagick 确认尺寸

content-length5000000 或尺寸不到 300x157 就重新导出。直接按 1200x630(1.91:1)设计,这样不会被裁。

4. X 缓存了旧版本(7 天缓存)

X 按 URL 缓存卡片数据,首次抓取后最长 7 天。你改完 meta 重发上线,但推文里还是旧图。2022/2023 年起 cards-dev.twitter.com/validator 已被关闭且一直没有替代品,没有公开的「强制重抓」按钮。

怎么判断:URL 粘到 opengraph.devmetatags.io——它们会即时抓取,显示当前 meta 实际输出的卡片。若验证器显示新图但 X 还在显示旧图,就是 X 缓存。

修复(按有效性排序):

  1. URL 加个 cache-buster 参数:推 https://yourdomain.com/article/foo?v=2 而不是裸 URL。X 按精确 URL 缓存,加了参数就被当成新页面。
  2. 等 7 天,缓存自然过期。
  3. 改 canonical URL(极少用,除非 slug 真换了)。X 是对重定向的最终落地 URL 做缓存,所以换一个 301 目标也会触发重抓。

5. 图在 auth / CDN 签名 / 防盗链后

twitter:image 指向 Cloudinary 签名 URL、需要 Referer 的 CDN,或登录墙后的路径。Twitterbot 拿到的是 401 / 403 / 302。

curl -A "Twitterbot/1.0" -I "https://cdn.yourdomain.com/og/article.png"
# 200 + image/png 表示 OK
# 401 / 403 / 302 表示被挡

修复:把 OG 图放进静态 public/ 目录,或在 CDN / WAF 上白名单 Twitterbot/1.0 user-agent。另外检查 robots.txt 里有没有 User-agent: TwitterbotDisallow: /——这会悄悄把 X 挡在门外,而 LinkedIn、Slack(用的是别的爬虫)照常工作,这正是「哪儿都行就 X 不行」的典型特征。

6. meta 标签被客户端 JavaScript 注入(SPA)

X 爬虫不执行 JavaScript。如果你的 og: / twitter: 标签是 React Helmet、Vue Meta、客户端路由下的 next/head,或任何在客户端 hydrate 的框架在加载后才加上的,那爬虫抓到初始 HTML 时找不到卡片标签,就只渲染裸链接。这也是为什么 DevTools(看的是实时 DOM)里一切正常、卡片却还是出不来。

怎么判断:跑 curl -s "https://yourdomain.com/article/foo" | grep -i 'twitter:card\|og:image'。如果这些标签不在 curl 的原始响应里、却出现在浏览器 Elements 面板,那就是 JS 注入的。

修复:把 meta 标签改成服务端渲染或静态预渲染(Next.js/Nuxt/Astro 的 SSR 或 SSG,或框架的 metadata API),或用 prerender / 动态渲染给爬虫单独吐一份只含 meta 的 HTML。标签必须出现在最初那份 HTML 响应里,且在 <head> 内。

把 Open Graph 当 fallback(2026 年的实际行为)

截至 2026 年 6 月,X 在缺 twitter: 命名空间时会回退读 Open Graph。如果 og:image 设得对,你只要再加一行 <meta name="twitter:card" content="summary_large_image" /> 就够,可以省掉 twitter:image。内容站的推荐最小集:

<meta property="og:title" content="文章标题" />
<meta property="og:description" content="一句话摘要" />
<meta property="og:image" content="https://yourdomain.com/og/article.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://yourdomain.com/articles/article-slug/" />
<meta property="og:type" content="article" />

<meta name="twitter:card" content="summary_large_image" />

只有在你确实想让 X 用一张和 Facebook/LinkedIn 不一样的图时,才单独设 twitter:image

最短修复路径

按命中率排序:

  1. twitter:card(如果缺) -> 修掉占比最大的「没卡片」情形
  2. twitter:image(或 og:image)改成绝对 https -> 第二大
  3. 确认标签在服务端渲染的 HTML 里,而不是 JS 注入 -> SPA 的隐形杀手
  4. 图重新导出成 1200x630 PNG/JPEG,文件 < 1MB -> 修掉尺寸/格式问题
  5. ?v=N 强刷,或等 7 天 -> 旧图情形
  6. CDN/WAF 白名单 Twitterbot、修 robots.txt、把图移进 public/ -> 被挡图的长尾

怎么确认已经修好

  1. 像爬虫那样抓页面,确认标签在原始 HTML 里:
    curl -s -A "Twitterbot/1.0" "https://yourdomain.com/articles/your-slug/" | grep -iE 'twitter:card|og:image'
  2. 像爬虫那样抓图片,确认干净的 200
    curl -A "Twitterbot/1.0" -I "https://yourdomain.com/og/your-image.png"
  3. 把 URL 粘到 opengraph.dev——它会即时重抓,反映的是你当前的标签,而不是 X 的缓存。
  4. 最终真相:用一个小号发这条 URL(在跟旧缓存较劲就加 ?v=2),确认大图真的渲染出来。

预防建议

  • 统一用 1200x630 PNG 或 JPEG,目标 < 500KB,从主域名(不要签名 CDN)出。
  • 用一个 helper 强制 og:image / twitter:image 一定是带协议的绝对 URL。
  • 确保卡片标签是服务端输出的,这样爬虫不跑 JavaScript 也能看到。
  • CI 加检查:每篇新文章的 OG 图 curl -A "Twitterbot/1.0" -I 确认 200、content-type 正确、content-length 小于 5MB。
  • 除非真要分平台用不同图,否则别设 twitter:image——让 X 回退读 og:image,少一个字段就少一处会漂移的地方。
  • OG 图按标题自动生成(@vercel/og、satori、puppeteer),从源头杜绝「忘加图」。

FAQ

Q:X 老的卡片验证器死了,现在怎么测? A:用 opengraph.devmetatags.io,都会即时抓你的 URL 并模拟 X 的渲染。要看绝对真相,用一个小号发一次然后截图。

Q:改完 meta 还是显示旧图,X 多久会重抓? A:X 卡片缓存大约 7 天,没有公开的强制刷新。最快的 workaround 是推的 URL 加 ?v=2——X 会当成新 URL 立刻重抓。

Q:og:imagetwitter:image 都要写吗? A:不必。截至 2026 年 6 月,X 把 og:image 当 fallback 在读。除非要分平台用不同图,否则只写一个就够。唯一必须的是 <meta name="twitter:card">

Q:DevTools 里标签明明是对的,卡片还是出不来? A:DevTools 显示的是 JavaScript 跑完后的实时 DOM;X 爬虫不跑 JavaScript。用 curl -s <url> | grep twitter:card 看原始 HTML。如果 curl 里没有、DevTools 里有,那就是 JS 注入的,必须挪到服务端渲染的 HTML 里。

Q:卡片有图但裁切得很怪。 A:summary_large_image 强制 1.91:1。源图是 1:1 或 4:3 会被居中裁切。重新导出成精确的 1200x630。

Q:动图 GIF 只显示第一帧? A:是的,X 会拿第一帧定格。如果第一帧是黑底标题卡,卡片就像没图。直接用静态 PNG/JPEG。

Q:私信里 unfurl 正常,推文里不行。 A:私信预览是另一套即时抓取的管线,不走 7 天卡片缓存;推文渲染走卡片管线。两边表现不一致,就确认是缓存问题不是 meta 问题。

相关阅读

标签: #SEO #排查 #排查 #结构化数据 #Twitter Card #OG 图