What is a link preview, and where does it come from?
When you paste a link into Telegram, WhatsApp, Slack or LinkedIn, the platform's server (or the sender's phone) downloads the page, reads a handful of tags in its <head>, and turns them into a card with a title, a short description and an image. Those tags are called Open Graph tags. Facebook introduced the protocol in 2010, and today almost every app that shows link previews reads it. X (Twitter) adds its own Twitter Card tags on top, and falls back to Open Graph when they're missing.
Two things about these bots explain almost every broken preview:
- They don't run JavaScript. They read the raw HTML your server sends. Tags that a React, Vue or Angular app adds in the browser are invisible to them.
- Each one has its own rules. WhatsApp drops heavy images, Slack prefers Twitter Card tags, LinkedIn hides the description, X needs
twitter:cardfor a large image. A preview that looks perfect in one app can be broken in another.
That's why this checker doesn't stop at reading your tags. It requests the page with the real User-Agent of every major bot, compares what each one gets back, checks the image against each platform's limits, and draws the preview the way each app would.
The recommended set of tags
This set produces a large, complete preview on every platform we test. Put it in the <head> of each page, with values specific to that page:
<title>Page title | Brand</title>
<meta name="description" content="One or two sentences about the page.">
<link rel="canonical" href="https://example.com/page">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Brand">
<meta property="og:title" content="Page title">
<meta property="og:description" content="One or two sentences about the page.">
<meta property="og:url" content="https://example.com/page">
<meta property="og:image" content="https://example.com/og/page.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="What the image shows">
<meta name="twitter:card" content="summary_large_image">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
Some details that matter more than they look:
- Use
propertyforog:*tags andnamefortwitter:*tags. - Use absolute URLs with
https://forog:imageandog:url. Relative paths break on several platforms. - Declare each tag once. When a theme and an SEO plugin both output
og:title, platforms disagree on which one wins. - You don't need
twitter:title,twitter:descriptionortwitter:imageif they'd match the Open Graph values. X and Slack fall back to Open Graph. Onlytwitter:cardis needed.
og:image size and format
Use a 1200×630 px JPG or PNG under 300 KB. That single image is shown as a large picture everywhere. Keep text and logos away from the edges, because platforms crop slightly differently.
| Platform | Large image when | Limits to watch |
|---|---|---|
| Wide image, about 600 px or wider | Under 600 KB (Meta's docs), under 300 KB to be safe. At least 300 px wide. JPG or PNG. | |
| Telegram | Wide image; small or square images become a thumbnail | Must load without cookies or login. |
| iMessage | Any og:image | HTTPS. Falls back to apple-touch-icon. |
| 600×315 px or larger | At least 200×200 px, up to 8 MB. | |
| X (Twitter) | twitter:card = summary_large_image | Under 5 MB. JPG, PNG, WebP or GIF. No SVG. |
| Any og:image | At least 200×200 px (1200×627 recommended), under 5 MB. | |
| Slack, Discord | twitter:card = summary_large_image | Otherwise shown as a small thumbnail. |
Never use SVG for og:image. No major platform renders it. WebP and AVIF are risky too: they work on some platforms and silently fail on others.
Why a link preview doesn't show: the usual causes
The site is a single-page app
If your site is built with React, Vue, Angular or Svelte without server-side rendering, the HTML your server sends is an almost empty shell like <div id="root"></div> plus a script. Browsers run the script; bots don't. Libraries like react-helmet only change the tags in the browser, which is too late. The fix is to render the tags on the server: use SSR (Next.js, Nuxt, SvelteKit, Angular SSR, Remix), prerender pages at build time, or serve prerendered HTML to bots. This checker detects app shells and tells you which framework it found.
The bots are blocked
Bot protection (Cloudflare Bot Fight Mode, WAF rules, rate limiters) often returns a 403 or a challenge page to crawlers. Then the platform has nothing to read. Because this tool fetches your page with every bot's User-Agent, it shows exactly which ones are refused. Slackbot, Twitterbot and LinkedInBot also obey robots.txt, so a broad Disallow blocks their previews too.
The image is the problem
Most "preview without picture" cases are about og:image: too heavy for WhatsApp, a relative URL, an SVG, hotlink protection that rejects requests from other domains, or an image URL that redirects to a login page.
You're looking at a cached preview
Platforms cache what they fetched. After a fix, the old preview can stay for days unless you ask for a refresh. The results include links to every platform's refresh tool.
Slow server
Bots wait only a few seconds. If the page takes longer to respond, the preview is skipped. Long redirect chains add to the delay.
How this checker works
- We download your page from our server nine times in parallel: once as a regular browser, and once with the User-Agent of each preview bot (facebookexternalhit, WhatsApp, TelegramBot, Twitterbot, Slackbot, Discordbot, LinkedInBot and Apple's iMessage fetcher). JavaScript never runs.
- We follow up to 5 redirects and show the chain, then compare the responses. If one bot gets a different status, redirect or set of tags, you'll see it.
- We download the preview image and measure its real file size, dimensions and format, and check whether any bot is refused.
- For each platform, we pick the title, description and image with that platform's own fallback rules, and draw the card with its layout and truncation.
- You get a list of errors, warnings and passed checks, each with a fix, plus a ready-to-paste block of tags filled in with your page's own values.