Open Graph and Twitter Cards: A Practical Guide to Social Previews
Why do social preview cards break across X, Telegram, WhatsApp, and other platforms? Learn how metadata, crawler behavior, absolute URLs, caching, and platform differences affect link previews.
This page is organized as a full read-through, from background to implementation and usage.
Why do social media preview cards break so often? You paste a link into X (Twitter), Telegram, or WhatsApp, and the image is missing, the title is wrong, or the card simply fails to render.
Most tutorials focus purely on HTML syntax: "Just add these <meta> tags and you are done." However, in production, ensuring social previews work is less about knowing the tags and more about understanding crawler behavior, caching, and network routing.
This guide breaks down the core concepts of Open Graph and Twitter Cards, followed by a production-oriented troubleshooting checklist.
Open Graph vs. Twitter Cards: The Basics
There are two primary protocols you need to satisfy to cover 99% of social platforms:
- Open Graph (OG): Originally developed by Facebook, this relies on the
propertyattribute (e.g.,<meta property="og:title" ...>). It is the universal standard used by Facebook, LinkedIn, WhatsApp, and Telegram. - Twitter Cards: X's proprietary protocol, which relies on the standard HTML
nameattribute (e.g.,<meta name="twitter:title" ...>).
The Fallback Mechanism
If X (Twitter) crawls a page and cannot find a specific twitter: tag (like twitter:title or twitter:description), it will automatically fall back to the corresponding og: tag.
However, there is one critical exception: twitter:card.
Open Graph has no equivalent to twitter:card. If you want a rich, large image preview on X, you MUST explicitly declare:
<meta name="twitter:card" content="summary_large_image">Without this, X will default to a small text-only card or no preview at all, regardless of your OG image.
Why Social Previews Really Break: The Crawler Layer
1. Scrapers Do Not Execute JavaScript
Facebook, X, Telegram, and WhatsApp use simple server-side scrapers. They do not execute JavaScript.
If you use a client-side Single Page Application (SPA) framework (like raw React or Vue) and inject your <meta> tags via document.head.appendChild on mount, the crawler will see an empty <head>. Your og:image and og:title must be present in the raw HTML response generated by Server-Side Rendering (SSR), Static Site Generation (SSG), or an edge gateway.
2. The Absolute URL Rule
You must use absolute URLs (https://...) for your images.
<!-- WRONG ❌ -->
<meta property="og:image" content="/images/share.jpg">
<!-- CORRECT ✅ -->
<meta property="og:image" content="https://example.com/images/share.jpg">Facebook's crawler explicitly forbids relative URLs. Telegram's crawler will silently fail if given a relative path.
Wait, but I used a relative path and Telegram still showed an image?
If Telegram fails to resolve the og:image, its crawler will aggressively scan the page's <body> and blindly scrape whatever <img> it thinks is large enough to be a fallback. This is a failure state, not a feature. Always use absolute URLs to guarantee the correct image is chosen.
3. The Image Must Be A Direct Link
Avoid placing your og:image behind a 301 or 302 redirect. Telegram, in particular, is notorious for abandoning the image fetch if it hits a redirect chain. The URL you provide in the metadata should return an HTTP 200 with the raw image bytes.
The Great Cache Nightmare
You update your og:image, deploy the site, paste the link into Telegram... and the old image still appears.
Social platforms cache preview metadata aggressively on their own servers to save bandwidth.
- Facebook: Caches aggressively. You can force a refresh using their Sharing Debugger.
- X (Twitter): Caches persistently. In 2022, X retired its Card Validator preview feature and removed the ability to manually clear the cache.
- Telegram / WhatsApp: Cache durations are undocumented and can last for extended periods.
How to Force a Cache Refresh
Since X and messaging apps no longer provide reliable "clear cache" tools, you must trick them into believing the URL is entirely new.
The Solution: Append a content-based query string to your image URL.
<!-- The scraper sees this as a brand new image and fetches it immediately -->
<meta property="og:image" content="https://example.com/share.jpg?v=a1b2c3d4">Do not use a raw deployment timestamp, as that will force the crawlers to re-fetch identical images on every build. Instead, use a hash of the image file (like an ETag) so the URL only changes when the actual image content changes.
The Preview Image Exists, But Can The Crawler Fetch It?
This is the most common blind spot for developers. You inspect the HTML, the og:image is there, the absolute URL is correct, yet the social card is blank.
Good diagnostics require checking the network boundary:
- Cloudflare Bot Fight Mode: Is your WAF aggressively blocking automated traffic? Social crawlers identify themselves via User-Agents like
facebookexternalhit,Twitterbot, orTelegramBot. If your firewall blocks them, the card fails. robots.txt: Does yourrobots.txtdisallow/images/? Some scrapers strictly obey these rules and will refuse to fetch your cover.- Content-Type: If your server returns
application/octet-streaminstead ofimage/jpegorimage/png, platforms like WhatsApp will discard the image. - Image Size Limitations: Large images or unsupported formats (like modern WebP or heavy GIFs) may fail or behave inconsistently across platforms and crawlers. Stick to highly compressed JPEGs or PNGs.
Next.js Implementation Example
If you are using the Next.js App Router, configuring standard social cards across all platforms is seamless using the unified Metadata API:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Your Page Title',
description: 'Your page description.',
openGraph: {
title: 'Your Page Title',
description: 'Your page description.',
url: 'https://example.com',
siteName: 'Your Site',
images: [
{
url: 'https://example.com/og-image.jpg?v=hash123',
width: 1200,
height: 630,
},
],
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image', // CRITICAL
title: 'Your Page Title',
description: 'Your page description.',
images: ['https://example.com/og-image.jpg?v=hash123'],
},
}Production Troubleshooting Checklist
Before launching a high-stakes campaign, run through this checklist:
- [ ] Does
curl -s https://your-site.com | grep "og:image"return a tag? (Proves SSR works). - [ ] Is the image URL an absolute
https://...path? - [ ] Does
curl -I https://your-site.com/image.jpgreturnHTTP 200(no redirects) andContent-Type: image/jpeg? - [ ] Is the image file size highly compressed to ensure broad compatibility?
- [ ] Is
twitter:cardexplicitly set tosummary_large_image? - [ ] Are crawler bots whitelisted in your WAF/Firewall?
Social previews are not just aesthetic—they drastically impact Click-Through Rates (CTR). By understanding how crawlers fetch and cache your data, you can stop fighting the HTML tags and start diagnosing the actual network path.
Need to verify if your headers and response status are bot-friendly? Run your URL through the OpsKitPro Website Check to analyze the raw HTTP response before you share.