og-image-kit 是做什么的?
目标:每个公开页面都拥有一张 1200×630 的分享图片以及正确的 og:* / twitter:* 标签,并且你已经通过 scripts/check_og.py 验证了它们,而不仅仅是写上去。
步骤 1 — 检测技术栈
在编写任何代码之前,先查看仓库:
| 信号 | 技术栈 | 跳转至 |
|---|---|---|
next 在 package.json 中且存在 app/(或 src/app/)目录 | Next.js App Router | 步骤 2A |
next 但只有 pages/ | Next.js Pages Router | 步骤 2C(静态)或添加 /api/og 并使用 ImageResponse |
astro 在 package.json 中 | Astro | 步骤 2B |
| 纯 HTML、Hugo、Jekyll、Vite SPA 或其他 | 静态站点 | 步骤 2C |
同时找到:网站的规范基础 URL(环境变量如 NEXT_PUBLIC_SITE_URL、astro.config.* 中的 site)、品牌色、Logo 以及使用的字体。如果无法找到,则询问用户。
步骤 2A — Next.js App Router
- 确保根布局导出
metadataBase(OG URL 必须是绝对路径):export const metadata = { metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000") } - 将
assets/nextjs/opengraph-image.tsx复制到app/opengraph-image.tsx(站点默认)。 编辑BRAND、颜色和标语。 - 对于动态路由(博客文章、产品),将其复制到相应的路由段中,例如
app/blog/[slug]/opengraph-image.tsx,并使用assets/nextjs/dynamic-opengraph-image.tsx, 该文件会读取params并从数据源加载标题。 在 Next.js 15+ 中,params是一个 Promise — 请对其使用await。 - 仅当 X(Twitter)卡片必须不同时,才添加
twitter-image.tsx;否则,当你在 metadata 中设置twitter: { card: "summary_large_image" }时,Next.js 会复用 OG 图片。 - 每页文本:在每个页面的
metadata/generateMetadata中设置title和description。 - 自定义字体:通过
fetch(new URL("./Inter-Bold.ttf", import.meta.url))或readFile加载 TTF/OTF 字体文件(不支持 WOFF2)。CJK 文本需要 CJK 字体文件,否则会显示为方块——请对其进行子集化以减小文件体积。
需注意的限制(next/og / satori):仅支持 flexbox(不支持 grid),每个有多个子元素的 div 都需要设置 display: "flex",保持 bundle 体积较小——Next.js 的 ImageResponse 文档列出了 JSX + CSS + 字体 + 图片 500 KB 的限制。大字体也会减慢渲染速度,因此请对其进行子集化。
步骤 2B — Astro
使用 assets/astro/og-endpoint.ts 作为 src/pages/og/[...slug].png.ts。它会在构建时使用 satori + @resvg/resvg-js(需要执行 npm i satori @resvg/resvg-js)为每个页面渲染 PNG 图片,然后从布局中引用它:
<meta property="og:image" content={new URL(`/og/${slug}.png`, Astro.site)} />
必须在 astro.config.mjs 中设置 Astro.site。字体文件:将 TTF 字体放入 src/assets/fonts/。
步骤 2C — 静态站点 / 其他情况
生成 PNG 图片并提交到仓库:
python3 scripts/generate_og.py --pages pages.json --out public/og --brand "Acme" --bg "#0f172a" --fg "#ffffff" --accent "#38bdf8"
pages.json 是一个包含 {"slug": "pricing", "title": "...", "subtitle": "..."} 的列表(参考 assets/pages.example.json)。需要 Pillow(pip install pillow)。对于品牌字体或 CJK 文本(例如 Noto Sans SC),使用 --font path/to/font.ttf 参数。然后将标签添加到每个页面(模板位于 references/meta-tags.md)。
步骤 3 — 必需的标签(每个页面)
<meta property="og:title" content="Page title" />
<meta property="og:description" content="一句话,不超过约 160 个字符" />
<meta property="og:image" content="https://example.com/og/page.png" /> <!-- 绝对 URL -->
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://example.com/page" />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" />
Next.js 会从 metadata + opengraph-image.tsx 自动生成这些标签;不要手动重复编写。
步骤 4 — 验证(不要跳过)
对运行中的站点(开发服务器、预览部署或生产环境)运行:
python3 scripts/check_og.py http://localhost:3000/ http://localhost:3000/pricing
python3 scripts/check_og.py --json https://example.com/ # 机器可读
它会获取每个页面,检查必需的标签,确保 og:image 是绝对 URL、可访问、返回图片类型的内容、宽高比约为 1.91:1、宽度至少 600 像素,且文件大小不超过 5 MB。任何错误都会导致退出码为 1。修复所有标记为 ERROR 的内容;WARN 项目为建议。
注意:社交平台会缓存预览。修复后,请使用平台自己的调试工具(LinkedIn Post Inspector、Facebook Sharing Debugger)重新抓取。X(Twitter)不再提供公开验证器;可以通过发布草稿/私信来检查。localhost URL 无法被这些工具抓取——请先部署。
图片的设计规则
- 1200×630,PNG 格式,文本保持在约 60 像素的安全边距内;标题不超过约 60 个字符,最多 3 行。
- 高对比度;图片在信息流中显示较小(约 500 像素宽)。请在 50% 缩放比例下测试。
- 包含品牌名称/Logo;避免使用小于 28 像素(在 1200 宽度下)的微小文字。
- 各平台的具体说明请参考
references/platform-specs.md。
完成条件
- 每个可索引的页面都返回 og:title、og:description、og:image(绝对 URL)、twitter:card
- 对于首页以及每种路由类型至少一个页面,
check_og.py的退出码为 0 - 图片看起来正确(打开一个 PNG 图片并查看)