DevToolBox免费
博客

Open Graph 和 Twitter Card 元标签:完整开发者参考

10 分钟阅读作者 DevToolBox

当你在 Facebook、Twitter、LinkedIn、Discord 或 Slack 上分享链接时,出现的富媒体预览卡片是由 Open Graph (OG) 元标签Twitter Card 标签驱动的。正确设置这些标签意味着更多点击、更高互动和更专业的外观。设置错误则意味着预览损坏、图片缺失和流量流失。本完整参考指南涵盖了每个标签、每个平台要求和每个常见错误——并附带所有主流框架的可复制代码。

使用我们的免费 Meta 标签生成器立即创建完美元标签 →

必备 Open Graph 标签

Open Graph 协议由 Facebook 于 2010 年创建,此后几乎被所有社交平台采用。这四个标签是任何页面的最低要求:

og:title

页面在社交分享卡片中显示的标题。保持在 60 个字符以内以获得最佳显示效果。

<meta property="og:title" content="How to Build a REST API with Node.js in 2026" />

og:description

预览卡片中标题下方显示的页面内容简介。目标 120-160 个字符。

<meta property="og:description" content="Step-by-step guide to building a production-ready REST API with Node.js, Express, and TypeScript. Includes auth, validation, and deployment." />

og:image

社交分享卡片中显示的图片。这是影响最大的标签——带图片的帖子获得 2-3 倍的互动。必须是绝对 URL。

<meta property="og:image" content="https://example.com/images/rest-api-guide-og.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="REST API with Node.js — Complete Guide" />
<meta property="og:image:type" content="image/png" />

og:url

页面的规范 URL。告诉平台将分享内容与哪个 URL 关联。

<meta property="og:url" content="https://example.com/blog/rest-api-nodejs" />

og:type

内容类型。常用值:website(默认)、article(博客文章)、productprofile

<!-- For a general page -->
<meta property="og:type" content="website" />

<!-- For a blog post / article -->
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2026-02-10T08:00:00Z" />
<meta property="article:author" content="https://example.com/about" />
<meta property="article:section" content="Technology" />
<meta property="article:tag" content="Node.js" />
<meta property="article:tag" content="REST API" />

其他 Open Graph 标签

除了基础标签外,这些标签让你对内容显示有更精细的控制:

<!-- Site name — shown above the title on Facebook -->
<meta property="og:site_name" content="DevToolBox" />

<!-- Locale — language_TERRITORY format -->
<meta property="og:locale" content="en_US" />
<meta property="og:locale:alternate" content="fr_FR" />
<meta property="og:locale:alternate" content="de_DE" />
<meta property="og:locale:alternate" content="es_ES" />
<meta property="og:locale:alternate" content="ja_JP" />

<!-- Video (for og:type = video.*) -->
<meta property="og:video" content="https://example.com/video.mp4" />
<meta property="og:video:width" content="1280" />
<meta property="og:video:height" content="720" />
<meta property="og:video:type" content="video/mp4" />

<!-- Audio -->
<meta property="og:audio" content="https://example.com/podcast-ep1.mp3" />
<meta property="og:audio:type" content="audio/mpeg" />

Twitter Card 标签

Twitter(现为 X)支持自己的卡片系统,有四种卡片类型。如果缺少 Twitter 专属标签,Twitter 会回退使用 Open Graph 标签。

Twitter Card 类型

卡片类型描述使用场景
summary小方形图片配标题和描述博客文章、一般页面
summary_large_image标题和描述上方的大图视觉内容、特色文章
player推文中嵌入的视频/音频播放器视频托管、播客
app移动应用直接下载卡片应用商店推广
<!-- Summary Card (small image) -->
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@yourusername" />
<meta name="twitter:creator" content="@authorname" />
<meta name="twitter:title" content="How to Build a REST API with Node.js" />
<meta name="twitter:description" content="Complete guide with auth, validation, and deployment." />
<meta name="twitter:image" content="https://example.com/images/rest-api-square.png" />
<meta name="twitter:image:alt" content="REST API Guide illustration" />

<!-- Summary Card with Large Image -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@yourusername" />
<meta name="twitter:title" content="How to Build a REST API with Node.js" />
<meta name="twitter:description" content="Complete guide with auth, validation, and deployment." />
<meta name="twitter:image" content="https://example.com/images/rest-api-wide.png" />

<!-- Player Card (video/audio) -->
<meta name="twitter:card" content="player" />
<meta name="twitter:player" content="https://example.com/embed/video123" />
<meta name="twitter:player:width" content="1280" />
<meta name="twitter:player:height" content="720" />

<!-- App Card -->
<meta name="twitter:card" content="app" />
<meta name="twitter:app:id:iphone" content="123456789" />
<meta name="twitter:app:id:googleplay" content="com.example.app" />
<meta name="twitter:app:name:iphone" content="My App" />
<meta name="twitter:app:name:googleplay" content="My App" />

各平台图片要求

每个平台有不同的图片尺寸要求。使用错误的尺寸会导致图片被裁剪、拉伸或缺失:

平台推荐尺寸最小尺寸最大文件宽高比格式
Facebook1200 x 630600 x 3158 MB1.91:1JPG, PNG, GIF, WebP
Twitter (summary)144 x 144144 x 1445 MB1:1JPG, PNG, GIF, WebP
Twitter (large image)1200 x 675300 x 1575 MB2:1 / 16:9JPG, PNG, GIF, WebP
LinkedIn1200 x 627200 x 2005 MB1.91:1JPG, PNG
Discord1200 x 630256 x 2568 MB1.91:1JPG, PNG, GIF
WhatsApp1200 x 630300 x 2005 MB1.91:1JPG, PNG
Slack1200 x 630250 x 2505 MB1.91:1JPG, PNG, GIF
Telegram1200 x 630200 x 2005 MB1.91:1JPG, PNG
Pinterest1000 x 1500600 x 60010 MB2:3JPG, PNG
iMessage1200 x 630300 x 3005 MB1.91:1JPG, PNG

完整 HTML 标记示例

这是一个包含所有 Open Graph 和 Twitter Card 标签的生产就绪 HTML head。复制并替换占位值:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />

  <!-- Primary Meta Tags -->
  <title>How to Build a REST API with Node.js in 2026</title>
  <meta name="description" content="Step-by-step guide to building a production-ready REST API with Node.js, Express, and TypeScript." />
  <link rel="canonical" href="https://example.com/blog/rest-api-nodejs" />

  <!-- Open Graph / Facebook -->
  <meta property="og:type" content="article" />
  <meta property="og:url" content="https://example.com/blog/rest-api-nodejs" />
  <meta property="og:title" content="How to Build a REST API with Node.js in 2026" />
  <meta property="og:description" content="Step-by-step guide to building a production-ready REST API with Node.js, Express, and TypeScript." />
  <meta property="og:image" content="https://example.com/images/rest-api-og.png" />
  <meta property="og:image:width" content="1200" />
  <meta property="og:image:height" content="630" />
  <meta property="og:image:alt" content="REST API with Node.js guide banner" />
  <meta property="og:site_name" content="My Dev Blog" />
  <meta property="og:locale" content="en_US" />

  <!-- Article-specific OG tags -->
  <meta property="article:published_time" content="2026-02-10T08:00:00Z" />
  <meta property="article:modified_time" content="2026-02-10T12:00:00Z" />
  <meta property="article:author" content="https://example.com/about" />
  <meta property="article:section" content="Web Development" />
  <meta property="article:tag" content="Node.js" />
  <meta property="article:tag" content="REST API" />
  <meta property="article:tag" content="TypeScript" />

  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:site" content="@yourusername" />
  <meta name="twitter:creator" content="@authorname" />
  <meta name="twitter:title" content="How to Build a REST API with Node.js in 2026" />
  <meta name="twitter:description" content="Step-by-step guide to building a production-ready REST API." />
  <meta name="twitter:image" content="https://example.com/images/rest-api-twitter.png" />
  <meta name="twitter:image:alt" content="REST API with Node.js guide banner" />
</head>
<body>
  <!-- Page content -->
</body>
</html>

框架专属示例

现代框架提供了管理元标签的内置 API。以下是在最流行框架中实现 OG 和 Twitter Card 标签的方法:

Next.js(App Router — Metadata API)

// app/blog/[slug]/page.tsx
import { Metadata } from 'next';

type Props = { params: { slug: string } };

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const post = await getPost(params.slug);

  return {
    title: post.title,
    description: post.excerpt,
    alternates: {
      canonical: `https://example.com/blog/${params.slug}`,
    },
    openGraph: {
      type: 'article',
      title: post.title,
      description: post.excerpt,
      url: `https://example.com/blog/${params.slug}`,
      siteName: 'My Dev Blog',
      locale: 'en_US',
      images: [
        {
          url: post.ogImage,      // Must be absolute URL
          width: 1200,
          height: 630,
          alt: post.title,
        },
      ],
      publishedTime: post.date,
      authors: [post.author],
      tags: post.tags,
    },
    twitter: {
      card: 'summary_large_image',
      site: '@yourusername',
      creator: '@authorname',
      title: post.title,
      description: post.excerpt,
      images: [
        {
          url: post.twitterImage,  // Can differ from OG image
          alt: post.title,
        },
      ],
    },
  };
}

export default function BlogPost({ params }: Props) {
  // Page component...
}

Nuxt 3(useSeoMeta)

<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute();
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`);

useSeoMeta({
  title: post.value.title,
  description: post.value.excerpt,
  ogType: 'article',
  ogTitle: post.value.title,
  ogDescription: post.value.excerpt,
  ogUrl: `https://example.com/blog/${route.params.slug}`,
  ogImage: post.value.ogImage,
  ogImageWidth: 1200,
  ogImageHeight: 630,
  ogImageAlt: post.value.title,
  ogSiteName: 'My Dev Blog',
  ogLocale: 'en_US',
  articlePublishedTime: post.value.date,
  articleAuthor: post.value.author,
  articleTag: post.value.tags,
  twitterCard: 'summary_large_image',
  twitterSite: '@yourusername',
  twitterCreator: '@authorname',
  twitterTitle: post.value.title,
  twitterDescription: post.value.excerpt,
  twitterImage: post.value.twitterImage,
  twitterImageAlt: post.value.title,
});
</script>

<template>
  <article>
    <!-- Page content -->
  </article>
</template>

静态 HTML

<!-- For static HTML sites, simply add meta tags in <head> -->
<!-- You can automate this with a build tool or templating engine -->

<!-- Example with Eleventy (11ty) Nunjucks template -->
<head>
  <title>{{ title }}</title>
  <meta name="description" content="{{ description }}" />
  <meta property="og:type" content="{% if layout == 'post' %}article{% else %}website{% endif %}" />
  <meta property="og:title" content="{{ title }}" />
  <meta property="og:description" content="{{ description }}" />
  <meta property="og:image" content="{{ site.url }}{{ ogImage }}" />
  <meta property="og:url" content="{{ site.url }}{{ page.url }}" />
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="{{ title }}" />
  <meta name="twitter:description" content="{{ description }}" />
  <meta name="twitter:image" content="{{ site.url }}{{ ogImage }}" />
</head>

<!-- Example with Hugo -->
<!-- layouts/partials/head.html -->
<meta property="og:title" content="{{ .Title }}" />
<meta property="og:description" content="{{ .Description }}" />
<meta property="og:image" content="{{ .Params.ogImage | absURL }}" />
<meta property="og:url" content="{{ .Permalink }}" />
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ .Title }}" />
<meta name="twitter:image" content="{{ .Params.ogImage | absURL }}" />

常见错误及修复方法

这些是开发者在社交元标签中最常遇到的问题。每一个都可能破坏你的分享预览:

#错误问题解决方案
1Relative image URLsog:image="/images/og.png" — platforms cannot resolve relative pathsAlways use absolute URLs: og:image="https://example.com/images/og.png"
2Missing image dimensionsFacebook may not display the image on first share without width/heightAlways include og:image:width and og:image:height tags
3HTTP instead of HTTPSMany platforms reject or warn about non-HTTPS image URLsServe all OG images over HTTPS. Use protocol-relative URLs as last resort
4Not handling cachePlatforms cache previews for hours/days; updating tags has no immediate effectUse platform debug tools to force refresh. Append ?v=2 for cache-busting
5Same tags on every pageEvery page shows the same preview card — confusing and poor CTRGenerate unique og:title, og:description, og:image per page
6Missing twitter:card tagTwitter will not render any card at all without this tagAlways include <meta name="twitter:card" content="summary_large_image" />
7Image too large (>8MB)Facebook silently fails; Twitter shows no imageOptimize images: compress to <1MB, use JPEG for photos, PNG for graphics
8Wrong image aspect ratioImage gets cropped awkwardly — text cut off, faces croppedUse 1200x630 (1.91:1) for OG, 1200x675 (16:9) for Twitter large image
9Duplicate og:image tagsUnpredictable behavior — platform may pick the wrong imageUse only one og:image tag per page (or intentionally provide multiple with primary first)
10Forgetting og:urlShares from different URLs (www vs non-www, query params) counted separatelySet og:url to the canonical URL to consolidate share counts

测试与调试工具

发布前务必验证你的元标签。每个平台都会积极缓存预览,修复标签后需要等待缓存过期或手动清除。

工具平台URL说明
Facebook Sharing DebuggerFacebook / Metadevelopers.facebook.com/tools/debug/Shows all OG tags, image preview, and errors. Click "Scrape Again" to refresh cache.
Twitter Card ValidatorTwitter / Xcards-dev.twitter.com/validatorPreview your Twitter Card. Validates tag structure. Requires Twitter login.
LinkedIn Post InspectorLinkedInlinkedin.com/post-inspector/Validates OG tags for LinkedIn. Click "Inspect" to refresh cached preview.
OpenGraph.xyzUniversalopengraph.xyzPreview how your URL appears on Facebook, Twitter, LinkedIn, Discord simultaneously.
Metatags.ioUniversalmetatags.ioReal-time preview editor. Edit tags and see Facebook/Twitter/Google previews instantly.
Social Share Preview (VS Code)DevelopmentVS Code ExtensionPreview OG tags directly in your IDE without deploying.
Ngrok / Cloudflare TunnelDevelopmentngrok.comExpose localhost to test with real platform validators during development.

动态 OG 图片

为每个页面生成唯一的社交图片可以显著提高点击率。每个页面都有展示其标题或数据的自定义卡片,而不是整个网站使用一张通用图片。

为什么动态 OG 图片很重要

GitHub、Vercel 和 DEV.to 等网站为每个页面生成 OG 图片。结果:它们的链接在社交信息流中脱颖而出。

<!-- Instead of this (same image for every page): -->
<meta property="og:image" content="https://example.com/default-og.png" />

<!-- Generate this (unique per page): -->
<meta property="og:image" content="https://example.com/api/og?title=How+to+Build+a+REST+API" />

使用 @vercel/og 生成

// app/api/og/route.tsx — Next.js Edge Route Handler
import { ImageResponse } from 'next/og';
import { NextRequest } from 'next/server';

export const runtime = 'edge';

export async function GET(req: NextRequest) {
  const { searchParams } = new URL(req.url);
  const title = searchParams.get('title') || 'Default Title';
  const description = searchParams.get('desc') || '';

  return new ImageResponse(
    (
      <div
        style={{
          width: '100%',
          height: '100%',
          display: 'flex',
          flexDirection: 'column',
          justifyContent: 'center',
          padding: '60px 80px',
          background: 'linear-gradient(135deg, #0f172a 0%, #1e293b 100%)',
          fontFamily: 'Inter, sans-serif',
        }}
      >
        <div style={{
          fontSize: 56,
          fontWeight: 800,
          color: '#f8fafc',
          lineHeight: 1.2,
          marginBottom: 20,
        }}>
          {title}
        </div>
        {description && (
          <div style={{
            fontSize: 24,
            color: '#94a3b8',
            lineHeight: 1.5,
          }}>
            {description}
          </div>
        )}
        <div style={{
          display: 'flex',
          alignItems: 'center',
          marginTop: 'auto',
          fontSize: 20,
          color: '#64748b',
        }}>
          example.com
        </div>
      </div>
    ),
    {
      width: 1200,
      height: 630,
    }
  );
}

// Usage in metadata:
// openGraph: { images: [`/api/og?title=${encodeURIComponent(post.title)}`] }

使用 Node Canvas 生成

// scripts/generate-og-images.ts — Build-time generation
import { createCanvas, registerFont } from 'canvas';
import fs from 'fs';
import path from 'path';

registerFont('fonts/Inter-Bold.ttf', { family: 'Inter', weight: 'bold' });

interface OGConfig {
  title: string;
  slug: string;
  theme?: string;
}

function generateOGImage(config: OGConfig): Buffer {
  const { title, theme = '#0f172a' } = config;
  const width = 1200;
  const height = 630;
  const canvas = createCanvas(width, height);
  const ctx = canvas.getContext('2d');

  // Background gradient
  const gradient = ctx.createLinearGradient(0, 0, width, height);
  gradient.addColorStop(0, theme);
  gradient.addColorStop(1, '#1e293b');
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, width, height);

  // Title text
  ctx.fillStyle = '#f8fafc';
  ctx.font = 'bold 52px Inter';
  const words = title.split(' ');
  let line = '';
  let y = 240;
  for (const word of words) {
    const test = line + word + ' ';
    if (ctx.measureText(test).width > 1040) {
      ctx.fillText(line.trim(), 80, y);
      line = word + ' ';
      y += 70;
    } else {
      line = test;
    }
  }
  ctx.fillText(line.trim(), 80, y);

  // Site name
  ctx.fillStyle = '#64748b';
  ctx.font = 'bold 24px Inter';
  ctx.fillText('example.com', 80, 560);

  return canvas.toBuffer('image/png');
}

// Generate for all posts
const posts: OGConfig[] = [
  { title: 'How to Build a REST API with Node.js', slug: 'rest-api-nodejs' },
  { title: 'TypeScript Generics Explained', slug: 'typescript-generics' },
];

for (const post of posts) {
  const buffer = generateOGImage(post);
  const outPath = path.join('public', 'og', `${post.slug}.png`);
  fs.mkdirSync(path.dirname(outPath), { recursive: true });
  fs.writeFileSync(outPath, buffer);
  console.log(`Generated: ${outPath}`);
}

使用 Cloudinary URL 转换

// Using Cloudinary URL-based image transformations
// No server-side code needed — just construct the URL

function getCloudinaryOGImage(title: string): string {
  const cloudName = 'your-cloud-name';
  const baseImage = 'og-template.png';  // Upload a background template first

  // URL-encode the title
  const encodedTitle = encodeURIComponent(title);

  // Cloudinary text overlay transformation
  return [
    `https://res.cloudinary.com/${cloudName}/image/upload`,
    // Resize to OG dimensions
    'w_1200,h_630,c_fill',
    // Add text overlay
    `l_text:Inter_52_bold:${encodedTitle}`,
    'co_rgb:f8fafc',       // Text color
    'g_west',              // Left-align
    'x_80,y_-40',          // Position
    'w_1040,c_fit',        // Max text width
    // Base image
    baseImage,
  ].join('/');
}

// Usage:
// <meta property="og:image"
//   content={getCloudinaryOGImage('How to Build a REST API with Node.js')} />

// Result URL looks like:
// https://res.cloudinary.com/your-cloud-name/image/upload/
//   w_1200,h_630,c_fill/
//   l_text:Inter_52_bold:How%20to%20Build%20a%20REST%20API/
//   co_rgb:f8fafc,g_west,x_80,y_-40,w_1040,c_fit/
//   og-template.png

常见问题

Open Graph 和 Twitter Card 标签有什么区别?

Open Graph 由 Facebook 创建,现在是 Facebook、LinkedIn、Discord、Slack、WhatsApp 等大多数平台使用的通用标准。Twitter Card 是 Twitter/X 的专有系统。Twitter 在没有专属标签时会回退使用 OG 标签,但两者都有可确保在所有平台上最佳显示。

我需要同时使用 Open Graph 和 Twitter Card 标签吗?

技术上不需要——Twitter 会读取 OG 标签作为后备。但最佳实践是两者都包含,因为 twitter:card 没有 OG 等价物且是 Twitter 显示卡片所必需的。

为什么分享链接时 OG 图片不显示?

最常见的原因:使用相对 URL 而非绝对 URL、图片 URL 返回 404、图片文件过大、平台缓存了旧版本、缺少 og:image:width 和 og:image:height 标签。

理想的 OG 图片尺寸是多少?

推荐尺寸为 1200 x 630 像素,宽高比 1.91:1。这在 Facebook、LinkedIn、Discord 等平台都表现良好。如果只能创建一张图片,1200 x 630 是最安全的通用选择。

如何强制平台刷新 OG 预览?

每个平台有自己的缓存清除工具:Facebook 使用 Sharing Debugger 点击"重新抓取",Twitter 使用 Card Validator,LinkedIn 使用 Post Inspector。

可以为 Facebook 和 Twitter 使用不同的图片吗?

可以。用 og:image 设置 Facebook 图片,用 twitter:image 设置 Twitter 图片。当两者都存在时 Twitter 优先使用 twitter:image。

试试我们的 Meta 标签生成器,创建完美的 OG 和 Twitter Card 标签 →

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

🏷️Meta Tag Generator👁️Open Graph Image Preview📐Schema Markup GeneratorFavicon Generator

相关文章

Favicon 完全指南 2026:所有尺寸、格式和生成方法

2026 年完整 Favicon 参考。ICO vs PNG vs SVG,所有浏览器和设备尺寸,暗色模式支持。

每个网站必备的 Meta 标签:HTML Meta Tag 完全指南

必备的 HTML meta 标签:SEO、Open Graph、Twitter Cards、安全性和性能优化。包含完整的复制粘贴模板。