OGKit

你必须写对的 OG / Twitter meta 标签

  • open-graph
  • meta-tags
  • twitter-cards

「要写 Open Graph」这件事大家都知道。真正翻车的,多半不是没听过协议,而是 DevTools 里看起来齐全,爬虫却仍看到相对路径图片、写错的 name=、两个互相打架的 title,或根本没有 Twitter Card。

本文是最小正确集:一段可粘贴的 HTML、逐字段说明与反例、与 <title> / meta description / JSON-LD 的回退关系、框架侧点到为止,以及用稳定 规则 id 做的自检清单。

更完整的工作流(预览 vs 官方 rescrape vs CI)见 Open Graph 到底在解决什么;空白卡片见 为什么你的 Open Graph 卡片一片空白

复制即用的最小片段

ogp.me 协议要求的四件套:og:titleog:typeog:urlog:image。上线实务几乎总要补 og:descriptionog:site_name,再加 twitter:card,好让 X 与部分客户端选对布局。

<head>
  <title>发版说明 · Acme</title>
  <meta
    name="description"
    content="本周修了什么,以及对你意味着什么。"
  />
  <link rel="canonical" href="https://example.com/blog/ship-notes" />

  <!-- Open Graph(用 property=,不要用 name=) -->
  <meta property="og:title" content="发版说明:本周修了什么" />
  <meta property="og:type" content="article" />
  <meta property="og:url" content="https://example.com/blog/ship-notes" />
  <meta
    property="og:image"
    content="https://example.com/og/ship-notes-1200x630.jpg"
  />
  <meta
    property="og:description"
    content="本周修了什么,以及分享链接时同事会看到什么。"
  />
  <meta property="og:site_name" content="Acme" />

  <!-- Twitter / X -->
  <meta name="twitter:card" content="summary_large_image" />
  <!-- 若不需要单独覆盖 OG 回退,可省略:
  <meta name="twitter:title" content="..." />
  <meta name="twitter:description" content="..." />
  <meta name="twitter:image" content="https://example.com/og/ship-notes-1200x630.jpg" />
  -->
</head>

这段里的硬约束:

  • 图片与 URL 必须是绝对 HTTPS(协议 + 主机 + 路径)。
  • og:url 有意与 rel=canonical 一致。
  • Open Graph 用 property="og:...";Twitter Card 惯例用 name="twitter:..."
  • 每个 key 只保留一个权威值。不要让主题、插件、框架各写一份不同的 og:title

逐字段说明(与反例)

og:titleog-title-missing

卡片标题。优先写「信息流里人愿意点」的句子,而不是堆关键词的 SEO 串。

反例 后果
缺失 / 空 回退到 <title> 或裸 URL
永远照抄 <title> 标签页与信息流职责不同——有时可以共用;默认照抄等于放弃写分享向标题(og-title-matches-title,info)
<!-- 弱:CMS slug 当标题 -->
<meta property="og:title" content="ship-notes-q3-final-v2" />

<!-- 更好:人话标题 -->
<meta property="og:title" content="发版说明:本周修了什么" />

og:typeog-type-missing

告诉爬虫这是什么对象。落地页:website;博客:article;商品页在真正是商品时用 product

反例 后果
缺失 部分富对象处理降级
自造值("landing""page" 当未知类型处理,没有收益
<meta property="og:type" content="website" />
<!-- 或 -->
<meta property="og:type" content="article" />

og:urlog-url-missing

分享的规范身份。点赞、展开会按它聚合 http/https、尾斜杠、追踪参数等变体。

反例 规则 / 影响
缺失 互动散落在多种 URL 上
相对路径(/blog/post og-url-relative
站点已是 HTTPS 仍写 http:// og-url-not-https
rel=canonical 不一致 og-url-canonical-mismatch
<!-- 错误 -->
<meta property="og:url" content="/blog/ship-notes" />

<!-- 正确 -->
<meta property="og:url" content="https://example.com/blog/ship-notes" />

og:imageog-image-missing

没有可用图时,不少平台只剩文字行,甚至几乎没有卡片样式。尽量用专用分享图(约 1200×630,约 1.91:1),不要拿 logo 凑数。

反例 规则 / 影响
缺失 / 空 空白或几乎无视觉权重
相对路径 og-image-relative-url——经常被直接丢掉
非 HTTPS og-image-not-https

图片工程(体积、跳转、Content-Type、裁切风险)留给系列后文。先把绝对 URL 写对。

og:descriptionog-description-missing

不在协议四件套里,但实务上是 warn 级必补。没有它时,客户端会回退 meta description 或抓正文——信息流文案变成抽奖。

写一两句给「正在刷时间线的人」看(大约 70–200 字符作起点;各平台截断不同)。

<!-- CMS 空占位往往比认真省略更糟 -->
<meta property="og:description" content="" />

og:site_nameog-site-name-missing

若干平台上的发布者标签。写品牌名,不要写页面标题。

<meta property="og:site_name" content="Acme" />

twitter:cardtwitter-card-missing / twitter-card-invalid

X 仍靠 twitter:card 选布局。合法值:summarysummary_large_imageappplayer。未知值等同缺失。

营销页与博客多半用 summary_large_image

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

回退现实: 很多客户端在缺少对应 twitter:* 时会用 og:title / og:description / og:image。这很省事——但不能因此省略 twitter:card。卡片类型并不能完全由 OG 推断出来。

若只想在 X 上用不同图或标题,再单独写 twitter:titletwitter:descriptiontwitter:image。否则让 OG 承载文案,仍声明 card 类型。

属性名与重复值陷阱

两类 bug 每周都在上线:

  1. name vs property —— OG 必须用 property="og:..."。写成 name="og:title" 是常见粘贴错误;主流解析器会直接忽略(meta-attribute-wrong)。
  2. 冲突的重复 —— 主题 + 插件 + 应用各输出一份不同的 og:title;爬虫取 first 还是 last 不可预期(meta-tag-conflicting-duplicates)。
<!-- 错:属性名不对 -->
<meta name="og:title" content="View Source 里看起来对,实际无效" />

<!-- 错:两个赢家 -->
<meta property="og:title" content="来自 CMS" />
<meta property="og:title" content="来自主题默认值" />

与 title / description / JSON-LD 的关系(回退链)

把它想成回退链,而不是单一真相源:

卡片字段 优先 常见回退
标题 og:title <title>,再是 URL
正文 og:description meta name="description",再是抓取正文
og:image 经常没有——别赌平台会找 logo
X 布局 twitter:card 无合法类型则降级 / 跳过
X 标题 / 图 twitter:title / twitter:image 有 OG 时通常回退到 og:*

JSON-LDArticleProduct 等)服务搜索与富结果,不能可靠替代 Open Graph 的分享卡片。若某平台 unfurl 根本不读你的 JSON-LD,schema 写得再漂亮,Slack 仍可能没图。

实务分工:

  • <title> + meta description → 标签页与搜索摘要
  • og:*(+ twitter:card)→ 分享表面
  • JSON-LD → 读它的引擎的结构化数据

事实对齐(产品名、日期、URL),但不要假设一层会填满另外两层。

框架侧(点到为止)

Next.js Metadata

Next 的 metadata / generateMetadata 在同时填 openGraphtwitter 时映射清晰:

import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "发版说明 · Acme",
  description: "本周修了什么,以及对你意味着什么。",
  alternates: { canonical: "https://example.com/blog/ship-notes" },
  openGraph: {
    title: "发版说明:本周修了什么",
    description: "本周修了什么,以及分享链接时同事会看到什么。",
    url: "https://example.com/blog/ship-notes",
    siteName: "Acme",
    type: "article",
    images: [
      {
        url: "https://example.com/og/ship-notes-1200x630.jpg",
        width: 1200,
        height: 630,
      },
    ],
  },
  twitter: {
    card: "summary_large_image",
    // 省略 title / description / images 时通常回退到 openGraph
  },
};

注意图片绝对 URL(或配置好的 metadataBase),并在 layout 合并后检查渲染出的 HTML里每个 property 仍只有一个值。

CMS 字段映射

编辑常填的字段 应输出为
SEO 标题 / 浏览器标题 <title>
SEO 描述 meta name="description"
分享标题 / 社交标题 og:title(可选再写 twitter:title
分享描述 og:description
分享图 / 社交图 绝对 URL 的 og:image
站点 / 品牌名(全局) og:site_name
规范 URL rel=canonical og:url

若 CMS 只有「SEO 标题」没有分享标题,标签页与信息流会完全一样。可作默认,但不是每篇发布文的最优解。

用规则 id 自检

对爬虫会看到的原始 HTML逐项核对(或放进 CI):

检查 规则
og:title og-title-missing
og:type og-type-missing
有绝对 HTTPS 的 og:url og-url-missingog-url-relativeog-url-not-https
og:url ≡ canonical og-url-canonical-mismatch
有绝对 HTTPS 的 og:image og-image-missingog-image-relative-urlog-image-not-https
og:description og-description-missing
og:site_name og-site-name-missing
有合法的 twitter:card twitter-card-missingtwitter-card-invalid
OG 用 property= 而非 name= meta-attribute-wrong
无冲突重复 meta-tag-conflicting-duplicates

完整目录:规则索引。各平台裁切与截断:平台档案

上线前清单

  • 协议四件套 + description + site_name + twitter:card 出现在原始 HTML
  • og:urlog:image 为绝对 HTTPS;图片 200 可抓、无登录墙
  • og:urlrel=canonical 一致
  • 每个 og:* / twitter:* 只有一个权威值
  • OG 用 property=;Twitter card 用 name=
  • description 是刻意写的文案,不是空字段

用公网 URL 扫一次

把线上页面贴进首页扫描。报告会按上面同一套规则 id 拆开——缺必填、相对路径图、错误属性、非法 Twitter card——而不是一张「看起来还行」的模拟卡。

第三方预览仍是近似。权威的抓取与缓存行为仍在各平台官方 debugger。先写对标签,再预览;若第一次抓取缓存了坏卡,再 rescrape。