DevToolBoxGRATIS
Blog

Open Graph & Twitter Card Meta Tags: Referensi Lengkap

10 menit bacaoleh DevToolBox

When you share a link on Facebook, Twitter, LinkedIn, Discord, or Slack, the rich preview card that appears is powered by Open Graph (OG) meta tags and Twitter Card tags. Getting these right means more clicks, more engagement, and a more professional appearance. Getting them wrong means broken previews, missing images, and lost traffic. This complete reference covers every tag, every platform requirement, and every common mistake — with copy-paste code for all major frameworks.

Generate perfect meta tags instantly with our free Meta Tag Generator →

Essential Open Graph Tags

Open Graph protocol was created by Facebook in 2010 and has since been adopted by nearly every social platform. These four tags are the absolute minimum for any page:

og:title

The title of your page as it should appear in the social share card. Keep it under 60 characters for best display. This can differ from your HTML <title> tag — use it to craft a more compelling social headline.

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

og:description

A brief summary of the page content shown below the title in the preview card. Aim for 120-160 characters. This is your chance to hook the reader into clicking.

<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

The image displayed in the social share card. This is the single most impactful tag — posts with images get 2-3x more engagement. Must be an absolute URL (not relative).

<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

The canonical URL of the page. This tells platforms which URL to associate with the shared content, preventing duplicate shares of the same content from different URLs.

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

og:type

The type of content. Common values: website (default), article (blog posts), product, video.movie, music.song, profile.

<!-- 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" />

Additional Open Graph Tags

Beyond the basics, these tags give you finer control over how your content appears:

<!-- 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 Tags

Twitter (now X) supports its own card system with four card types. Twitter will fall back to Open Graph tags if Twitter-specific tags are missing, but dedicated tags give you full control.

Twitter Card Types

Card TypeDescriptionUse Case
summarySmall square image with title and descriptionBlog posts, articles, general pages
summary_large_imageLarge prominent image above title and descriptionVisual content, featured articles, landing pages
playerInline video/audio player embedded in the tweetVideo hosting, podcasts, music platforms
appDirect download card for mobile applicationsApp Store / Google Play promotion
<!-- 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" />

Image Requirements by Platform

Every platform has different image size requirements. Using the wrong dimensions results in cropped, stretched, or missing images. Here is the definitive reference:

PlatformRecommended SizeMinimum SizeMax File SizeAspect RatioFormats
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

Complete HTML Markup Example

Here is a production-ready HTML head with every Open Graph and Twitter Card tag you need. Copy this and replace the placeholder values:

<!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>

Framework-Specific Examples

Modern frameworks provide built-in APIs for managing meta tags. Here is how to implement OG and Twitter Card tags in the most popular frameworks:

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>

Static HTML / Vanilla

<!-- 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 }}" />

Common Mistakes & How to Fix Them

These are the most frequent issues developers encounter with social meta tags. Each one can break your share previews:

#MistakeProblemSolution
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

Testing & Debugging Tools

Always validate your meta tags before publishing. Each platform caches previews aggressively, so fixing tags after sharing means waiting for cache expiration or manually clearing it.

ToolPlatformURLNotes
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.

Dynamic OG Images

Generating unique social images per page dramatically increases click-through rates. Instead of one generic image for your entire site, each page gets a custom card showing its title, description, or data.

Why Dynamic OG Images Matter

Sites like GitHub, Vercel, and DEV.to generate per-page OG images. The result: their links stand out in social feeds because every share looks unique and professional.

<!-- 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" />

Generating with @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)}`] }

Generating with 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}`);
}

Using Cloudinary URL Transforms

// 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

Frequently Asked Questions

What is the difference between Open Graph and Twitter Card tags?

Open Graph (OG) was created by Facebook and is now the universal standard used by Facebook, LinkedIn, Discord, Slack, WhatsApp, Telegram, and most other platforms. Twitter Cards are Twitter/X's proprietary system. The key difference: Twitter Card tags use the twitter: prefix while OG tags use the og: prefix. Twitter will fall back to OG tags if Twitter-specific tags are absent, but having both ensures optimal display on all platforms.

Do I need both Open Graph and Twitter Card tags?

Technically, no — Twitter will read OG tags as a fallback. However, including both is best practice because: (1) twitter:card has no OG equivalent and is required for Twitter to display a card at all, (2) you may want different titles or descriptions optimized for each platform, and (3) some Twitter card types (player, app) have no OG equivalent.

Why is my OG image not showing when I share my link?

The most common causes are: (1) Using a relative URL instead of an absolute URL — og:image must start with https://, (2) The image URL returns a 404 or redirect, (3) The image file is too large (Facebook limits to 8MB), (4) The platform has cached an old version — use the platform's debug tool to clear the cache, (5) Missing og:image:width and og:image:height tags which some platforms require for first-time fetches.

What is the ideal OG image size?

The recommended size is 1200 x 630 pixels with a 1.91:1 aspect ratio. This works well across Facebook, LinkedIn, Discord, and most platforms. For Twitter's summary_large_image, use 1200 x 675 (16:9). If you can only create one image, 1200 x 630 is the safest universal choice.

How do I force platforms to refresh my OG preview?

Each platform has its own cache-clearing tool: Facebook — use the Sharing Debugger and click "Scrape Again". Twitter — use the Card Validator to fetch a fresh preview. LinkedIn — use the Post Inspector to refresh the cache. For other platforms like Discord and Slack, you may need to wait for the cache to expire naturally (usually 24-48 hours) or append a query string like ?v=2 to your URL.

Can I use different images for Facebook and Twitter?

Yes. Set your Facebook image with og:image and your Twitter image with twitter:image. Twitter will prefer twitter:image over og:image when both are present. This is useful because the optimal aspect ratios differ: Facebook prefers 1.91:1 (1200x630) while Twitter's large image card uses 2:1 (1200x600) or 16:9 (1200x675).

Try our Meta Tag Generator to create perfect OG & Twitter Card tags →

𝕏 Twitterin LinkedIn
Apakah ini membantu?

Tetap Update

Dapatkan tips dev mingguan dan tool baru.

Tanpa spam. Berhenti kapan saja.

Coba Alat Terkait

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

Artikel Terkait

Panduan Favicon 2026: Semua Ukuran dan Format

Referensi lengkap favicon 2026.

Meta Tags yang Dibutuhkan Setiap Website: Panduan Lengkap Meta Tag HTML

Meta tag HTML penting untuk SEO, Open Graph, Twitter Cards, keamanan, dan performa. Termasuk template lengkap siap pakai.