Open Graph is the metadata protocol that turns a URL into a rich social card on Facebook, LinkedIn, X, Slack, Discord, WhatsApp, iMessage, Telegram, and every AI chat that unfurls links. Add four <meta property="og:..." /> tags to your <head> and a page goes from a naked blue hyperlink to a 1200×630 preview with a title, description, and image. Open Graph was published by Facebook in 2010 and remains the de-facto standard in 2026: the ogp.me specification hasn't changed, but platform rendering rules, image size expectations, and the rise of LLM-powered link unfurling have reshaped the best practices around it.
Why Does Open Graph Matter?
Open Graph is the single highest-leverage change you can make to how your URLs perform outside your own site. The cost is four lines of HTML; the upside compounds across every channel a link can be shared in.
- Click-through rate. A rich preview card lifts CTR on shared links by roughly 30–40% over a naked URL. On a link that gets 10k social impressions, that is 3–4k extra visits per post.
- First impression for free traffic. Most visitors from LinkedIn, Slack, Reddit, Discord, iMessage, and X see your OG card before they ever see your site. It is your real homepage for 60–80% of social sessions.
- Brand consistency. Without OG tags, every platform guesses — a random image from your page, a truncated
<title>, or nothing at all. With OG tags, every platform shows the exact title, image, and description you chose. - AI and LLM citation. GPTBot, ClaudeBot, PerplexityBot, and Google's AI crawlers read
og:title,og:description, andarticle:modified_timewhen choosing which pages to surface in answers. Fresh, well-described pages get cited; stale or unlabeled ones get skipped. - Indirect SEO. OG is not a Google ranking signal, but the downstream effects are: more clicks from shares → more branded search → longer sessions → stronger authority signals for the pages Google does rank.
- Share consolidation. A canonical
og:urlmakes likes, shares, and reactions aggregate to one URL instead of fragmenting across UTM and tracking variants. - It is table stakes in 2026. Missing OG tags read as neglect — the social equivalent of a broken favicon. Competitors have them; readers notice when you don't.
What Is the Open Graph Protocol?
Open Graph (OG) is a set of <meta> tags placed inside the <head> of an HTML document. Each tag uses a property="og:*" attribute instead of the usual name="...", because Open Graph is an RDFa vocabulary — a structured way to attach semantic data to a page. When a platform fetches your URL, its crawler parses these tags and uses them to render a preview card.
<meta property="og:type" content="website" />
<meta property="og:url" content="https://env.dev/guides/opengraph" />
<meta property="og:title" content="Open Graph Protocol: Complete Guide" />
<meta property="og:description" content="How to use Open Graph meta tags in 2026." />
<meta property="og:image" content="https://env.dev/og/opengraph.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Open Graph guide cover, 1200x630" />Two critical rules: tags must be server-rendered in the initial HTML — most social crawlers do not execute JavaScript, so client-side frameworks that inject meta tags with useEffect produce empty previews. And URLs must be absolute HTTPS: every major platform drops http:// images silently.
Which Open Graph Tags Are Required?
The spec defines four required properties. Everything else is optional but compounding — the more you add, the better every platform renders your card.
| Tag | Required? | Purpose | Notes |
|---|---|---|---|
| og:title | Yes | Preview headline | Keep ≤ 60 chars desktop, 40 mobile; no brand suffix |
| og:type | Yes | Content category | website, article, video.movie, music.song, profile, book |
| og:image | Yes | Preview image URL | Absolute HTTPS, JPG/PNG, 1200×630, ≤ 5 MB |
| og:url | Yes | Canonical URL | Strip query params; this is where shares aggregate |
| og:description | Recommended | Preview body | 2–4 sentences, ≤ 200 chars |
| og:site_name | Recommended | Site identity | Shown above the card on most platforms |
| og:locale | Optional | Primary language | e.g. en_US; add og:locale:alternate for others |
| og:image:width | Recommended | Image width in px | Lets crawlers render on first share |
| og:image:height | Recommended | Image height in px | Prevents async re-fetch delays |
| og:image:alt | Recommended | Image alt text | Screen readers + AI crawler context |
The og:type value unlocks an extra namespace of properties. For articles, add article:published_time, article:modified_time, article:author, and article:tag. These feed directly into LinkedIn's freshness signal and are read by LLM crawlers when citing content.
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2026-04-22T09:00:00Z" />
<meta property="article:modified_time" content="2026-04-22T09:00:00Z" />
<meta property="article:author" content="https://env.dev/about" />
<meta property="article:section" content="Web Standards" />
<meta property="article:tag" content="opengraph" />
<meta property="article:tag" content="seo" />What Are the Correct OG Image Dimensions in 2026?
One image at 1200×630 pixels (1.91:1) satisfies every major platform — Facebook, LinkedIn, Discord, Slack, WhatsApp, iMessage, and Telegram all render it cleanly. X/Twitter prefers 1200×675 (16:9) but crops 1200×630 with minimal loss. Going below 600×315 forces a fallback to the small summary card on most networks.
| Platform | Recommended size | Aspect ratio | Notes |
|---|---|---|---|
| 1200 × 630 | 1.91:1 | Minimum 600×315; max 8 MB | |
| 1200 × 627 | 1.91:1 | Cache lives ~7 days; hard to force refresh | |
| X / Twitter | 1200 × 675 | 16:9 | Works with 1200×630; falls back to og:image |
| Discord | 1200 × 630 | 1.91:1 | Max 8 MB; supports GIF and WebP |
| Slack | 1200 × 630 | 1.91:1 | Honors og:image:alt for screen readers |
| 1200 × 630 | 1.91:1 | ≤ 300 KB or it may be dropped on mobile | |
| iMessage | 1200 × 630 | 1.91:1 | Uses Apple Link Presentation; respects og:image |
| Telegram | 1200 × 630 | 1.91:1 | Caches aggressively; append ?v=2 to URL to bust |
File Format and Size Rules
- JPG or PNG — universally supported. PNG for logos and text, JPG for photos.
- WebP — works on Facebook, X, LinkedIn, Slack, Discord, but not on every older crawler. Safe as a primary, risky as the only format.
- AVIF — only Facebook renders it reliably in 2026. Don't use it as your og:image.
- GIF — shows the first frame as a static image on most platforms. Only Discord animates it.
- Target file size ≤ 300 KB for WhatsApp compatibility; hard limit is 8 MB on Facebook and Discord.
How Do X (Twitter) Cards Work With Open Graph?
X/Twitter runs its own Twitter Cards protocol, but the crawler falls back to Open Graph when a Twitter-specific tag is missing. That means you only need one extra tag to opt into the large image layout: twitter:card. Everything else can reuse your OG tags.
<!-- OpenGraph tags (shared with all other platforms) -->
<meta property="og:title" content="Open Graph Protocol: Complete Guide" />
<meta property="og:description" content="How to use Open Graph meta tags in 2026." />
<meta property="og:image" content="https://env.dev/og/opengraph.png" />
<meta property="og:url" content="https://env.dev/guides/opengraph" />
<!-- Twitter-specific additions -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@envdotdev" />
<meta name="twitter:creator" content="@envdotdev" />
<!-- twitter:title, twitter:description, twitter:image are all optional -->
<!-- If omitted, X will fall back to og:title / og:description / og:image -->Note the attribute difference: Twitter Cards use name="twitter:..." (standard HTML meta attribute), while Open Graph uses property="og:..." (RDFa). Both are valid HTML5 and both work in the same <head>.
| twitter:card value | Layout | When to use |
|---|---|---|
| summary | Small square thumbnail + text | Short posts, profile links |
| summary_large_image | Full-width 1.91:1 image + text | Blog posts, articles, marketing pages (default choice) |
| app | App Store / Play Store card | Mobile app landing pages |
| player | Embedded video / audio player | Video content with a playable stream |
How Do You Generate Dynamic OG Images?
A static site-wide image is fine for a landing page but looks amateur on a blog. Every blog post sharing the homepage image is one of the most visible signals of poor Open Graph implementation. Dynamic OG images — generated on demand per URL — are the 2026 default for any content site. The two common approaches:
1. Runtime image generation (JSX → PNG)
Vercel's @vercel/og library (bundled into next/og) uses Satori to render a JSX tree to SVG, then Resvg to rasterize to PNG — all inside an edge function. The output is cached at the CDN, so the second render is essentially free.
import { ImageResponse } from 'next/og';
export const runtime = 'edge';
export async function GET(req: Request, { params }: { params: { slug: string } }) {
const post = await getPost(params.slug);
return new ImageResponse(
(
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100%',
background: '#0b1020', color: 'white', padding: 80, justifyContent: 'space-between' }}>
<div style={{ fontSize: 72, fontWeight: 700, lineHeight: 1.1 }}>{post.title}</div>
<div style={{ fontSize: 28, opacity: 0.7 }}>env.dev · {post.date}</div>
</div>
),
{ width: 1200, height: 630 },
);
}Then point og:image at the route: <meta property="og:image" content="https://env.dev/og/my-post" />. Constraints: only flexbox layouts, max 500 KB bundle including fonts, and only .ttf / .otf / .woff fonts.
2. Build-time prerendering
For static sites, render images at build with Playwright or Satori and commit the PNGs. Zero runtime cost, but every content change triggers a rebuild. Works well when content volume is low or the build is already fast.
import satori from 'satori';
import { Resvg } from '@resvg/resvg-js';
import { writeFile } from 'node:fs/promises';
for (const post of await loadAllPosts()) {
const svg = await satori(
{ type: 'div', props: { style: { /* ... */ }, children: post.title } },
{ width: 1200, height: 630, fonts: [/* ttf buffer */] },
);
const png = new Resvg(svg).render().asPng();
await writeFile(`public/og/${post.slug}.png`, png);
}How Do Open Graph Tags Affect SEO?
Open Graph tags are not a direct Google ranking signal — Google uses its own <title>, <meta name="description">, and structured data. The SEO impact is indirect but measurable:
- Better previews lift click-through rate on shared links — industry benchmarks put the uplift at +30–40% vs. a naked URL.
- More clicks → more branded search volume and return visits → signals Google associates with authority.
- LinkedIn, Reddit, and Hacker News all strip tracking parameters differently. A canonical
og:urlmakes shares aggregate to one URL instead of fragmenting social proof across UTM variants. - AI crawlers (GPTBot, ClaudeBot, PerplexityBot) read
article:published_timeandog:descriptionwhen deciding whether to cite a page. Content older than three months without a refreshedarticle:modified_timesees meaningful AI citation drops.
Open Graph does not replace JSON-LD structured data. The two complement each other: OG drives the social card, Schema.org Article drives the Google rich result. Set both with matching titles, descriptions, and dates.
<meta property="og:title" content="Open Graph Protocol: Complete Guide" />
<meta property="og:description" content="How to use Open Graph meta tags in 2026." />
<meta property="article:published_time" content="2026-04-22T09:00:00Z" />
<meta property="article:modified_time" content="2026-04-22T09:00:00Z" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Open Graph Protocol: Complete Guide",
"description": "How to use Open Graph meta tags in 2026.",
"datePublished": "2026-04-22",
"dateModified": "2026-04-22",
"author": { "@type": "Organization", "name": "env.dev" }
}
</script>How Do You Debug and Monitor Open Graph Tags?
Every platform caches previews — typically 24–72 hours on Facebook, up to 7 days on LinkedIn, and indefinitely on Slack and Telegram until a user-side clear. Debugging means two things: inspecting what a crawler sees, and forcing a refresh after you change a tag.
Facebook Sharing Debugger
Parses your URL, shows the exact OG tags seen, and has a "Scrape Again" button that bumps the cache immediately.
developers.facebook.com/tools/debugLinkedIn Post Inspector
Previews the LinkedIn card. Hitting Inspect bumps the cache, but LinkedIn may still serve the old image for up to a week.
linkedin.com/post-inspectorX Card validator
Deprecated in 2023. Use opengraph.xyz, microlink.io, or a throwaway post in your drafts to preview.
opengraph.xyzSlack / Discord / WhatsApp
No official debugger. Send the URL to yourself in a private channel — but remember these platforms cache aggressively.
self-testcurl -A "facebookexternalhit"
See exactly what a crawler sees. Swap the user-agent for Twitterbot, LinkedInBot, Slackbot-LinkExpanding, WhatsApp, TelegramBot.
CLICache-buster query
Append ?v=2 to og:url and og:image when you genuinely change content — platforms treat new query strings as new URLs.
manual# Facebook
curl -sL -A "facebookexternalhit/1.1" https://env.dev/guides/opengraph \
| grep -Eo '<meta (property|name)="(og|twitter):[^"]+" content="[^"]+"'
# X / Twitter
curl -sL -A "Twitterbot/1.0" https://env.dev/guides/opengraph | grep 'twitter:'
# LinkedIn
curl -sL -A "LinkedInBot/1.0 (compatible; Mozilla/5.0; Apache-HttpClient +http://www.linkedin.com)" \
https://env.dev/guides/opengraph | grep 'og:'
# Slack
curl -sL -A "Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)" \
https://env.dev/guides/opengraph | grep 'og:'For production monitoring, snapshot your OG tags in a scheduled job and alert when og:image returns a non-200 or the tag shape drifts. A single missing tag on a popular page can cost thousands of impressions per day of social traffic.
Tools to Validate and Monitor Your Setup
Two third-party tools cover the one-off and the continuous case:
- opengraph.xyz — paste any URL and get a side-by-side preview of how it renders on Facebook, X, LinkedIn, Discord, Slack, and more. Useful for a quick sanity check before you ship.
- sharescan.io — continuous monitoring for Open Graph regressions. Runs post-deploy checks via GitHub Actions, scores every page, diffs metadata between releases, and routes regressions to Slack. Free 10-URL scan without signup.
What Are Common OG Mistakes and How to Fix Them?
| Mistake | Symptom | Fix |
|---|---|---|
| Tags injected by JS | Preview is blank or shows the fallback homepage card | Render tags in SSR/SSG initial HTML; never in useEffect |
| Relative image URL | No image in preview on any platform | Always use absolute https:// URL for og:image |
| Same og:image on every page | Homepage image appears on every blog post share | Generate dynamic images per URL (see section above) |
| Missing og:image:width / height | Image shows on second share but not first | Hardcode width/height so the crawler skips async parsing |
| Image > 300 KB | WhatsApp drops the preview entirely on mobile | Optimize to ≤ 300 KB; prefer JPG over PNG for photos |
| Cached old preview after edit | New title/image shows in HTML but not on Facebook | Use Sharing Debugger "Scrape Again" or append ?v=2 to og:url |
| Duplicate title/description tags | Random tag wins; preview is inconsistent across platforms | Keep one og:* tag per property; audit with curl or a debugger |
| Missing twitter:card | X shows a tiny summary card instead of the large image | Add <meta name="twitter:card" content="summary_large_image"> |
| og:url differs from canonical | Shares aggregate across multiple URLs; social counters fragment | og:url must equal <link rel="canonical"> |
| HTTP image instead of HTTPS | Preview image never renders on modern platforms | Serve OG images over HTTPS; no exceptions |
How Do Open Graph Tags Look in Different Frameworks?
Every modern meta-framework has a first-class API for OG tags — the key is that they render into the initial SSR HTML, not the client bundle.
// app/guides/opengraph/page.tsx
export const metadata = {
title: 'Open Graph Protocol: Complete Guide',
description: 'How to use Open Graph meta tags in 2026.',
openGraph: {
type: 'article',
url: 'https://env.dev/guides/opengraph',
title: 'Open Graph Protocol: Complete Guide',
description: 'How to use Open Graph meta tags in 2026.',
images: [{ url: 'https://env.dev/og/opengraph', width: 1200, height: 630, alt: 'Guide cover' }],
publishedTime: '2026-04-22T09:00:00Z',
modifiedTime: '2026-04-22T09:00:00Z',
},
twitter: { card: 'summary_large_image', site: '@envdotdev' },
};// apps/web/src/routes/guides/opengraph.tsx
export const Route = createFileRoute('/guides/opengraph')({
head: () => ({
meta: [
{ title: 'Open Graph Protocol: Complete Guide' },
{ name: 'description', content: 'How to use Open Graph meta tags in 2026.' },
{ property: 'og:type', content: 'article' },
{ property: 'og:url', content: 'https://env.dev/guides/opengraph' },
{ property: 'og:title', content: 'Open Graph Protocol: Complete Guide' },
{ property: 'og:description', content: 'How to use Open Graph meta tags in 2026.' },
{ property: 'og:image', content: 'https://env.dev/og/opengraph.png' },
{ property: 'og:image:width', content: '1200' },
{ property: 'og:image:height', content: '630' },
{ name: 'twitter:card', content: 'summary_large_image' },
],
}),
component: OpengraphRoute,
});---
// src/layouts/Base.astro
const { title, description, image = '/og/default.png', type = 'website' } = Astro.props;
const url = new URL(Astro.url.pathname, Astro.site).toString();
const ogImage = new URL(image, Astro.site).toString();
---
<head>
<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:type" content={type} />
<meta property="og:url" content={url} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImage} />
<meta name="twitter:card" content="summary_large_image" />
</head>Frequently Asked Questions
Do Open Graph tags affect Google SEO rankings?
No, not directly. Google does not use og:title or og:description as ranking signals — it has its own <title> and meta description. The impact is indirect: better social previews lift click-through rate by roughly 30-40%, which increases branded traffic and return visits, which are signals Google does care about. Also, AI crawlers that feed LLM answers read Open Graph tags to decide which pages to cite.
What happens if I only add og:title, og:description, og:image, and og:url?
That is the correct minimum. The spec also requires og:type, but most platforms default to "website" when it is missing. For articles, add og:type="article" plus article:published_time and article:modified_time so LinkedIn and AI crawlers can sort by freshness.
What is the ideal OG image size in 2026?
1200 × 630 pixels at a 1.91:1 aspect ratio. One image at this size renders correctly on Facebook, LinkedIn, Discord, Slack, WhatsApp, iMessage, and Telegram, and is cropped acceptably by X. Keep the file under 300 KB if you want WhatsApp to show it on mobile.
Why does Facebook show an old preview after I update my page?
Facebook caches previews for 24-72 hours, keyed by og:url. Open the Sharing Debugger at developers.facebook.com/tools/debug, paste the URL, and click "Scrape Again". If the image URL itself changed, append a query parameter like ?v=2 — platforms treat new URLs as fresh content.
Do I need Twitter Cards if I already have Open Graph tags?
You need one extra tag: <meta name="twitter:card" content="summary_large_image">. Without it, X defaults to the small summary card even if you have a 1200×630 image. Everything else — title, description, image — falls back to your og:* tags automatically.
Can I set Open Graph tags with JavaScript?
Technically yes, practically no. Most social crawlers (Facebook, LinkedIn, X, Slack, WhatsApp) do not execute JavaScript and will see an empty <head>. Your OG tags must be in the server-rendered HTML. Use SSR, SSG, or a head manager that outputs tags during the initial response.
How do I generate a different OG image for every page?
Use a dynamic image generator. On Next.js or edge-function platforms, @vercel/og renders JSX to PNG at request time and caches the result at the CDN. On static sites, pre-render images at build time with Satori + Resvg. Either way, point og:image at a per-URL path like /og/{slug}.
What is the difference between og:url and the canonical link tag?
Functionally they should be identical — both declare the canonical URL for the page. og:url is the social aggregation target (where likes and shares accrue), and <link rel="canonical"> is the search engine signal. Always keep them in sync; mismatches split social counters across URL variants.
References
- The Open Graph Protocol (ogp.me) — the original and canonical specification, including all og:type namespaces and structured properties
- Facebook Sharing for Webmasters — Meta's official guide to Open Graph tags, crawler behavior, and caching rules
- Facebook Sharing Debugger — inspect what Facebook's crawler sees and force a cache refresh with Scrape Again
- LinkedIn Post Inspector — preview and debug how your page renders when shared on LinkedIn
- X Cards markup reference — all twitter:card values, required fields, and Open Graph fallback behavior
- Vercel @vercel/og documentation — runtime OG image generation with JSX, ImageResponse, and edge caching
- Satori — JSX to SVG renderer — the engine behind @vercel/og; useful for build-time OG image generation
- Schema.org Article — the structured-data counterpart to og:type="article", used for Google rich results
- Ahrefs — Open Graph Meta Tags — practical tag-by-tag walkthrough with screenshots of real social previews