You shipped the post. You set og:image. You opened your phone, pasted the link in iMessage, and got a sad little blue underline with no preview. Or worse: it works on Twitter, but LinkedIn shows the wrong image, and Slack unfurls the title with a blank gray rectangle where the picture should be.
This is the most frustrating bug in publishing because every platform fails for a different reason and there is no global "purge" button. The good news: there are exactly nine root causes, and each one has a fix you can ship in under five minutes. This is the checklist I run every time a link preview breaks in 2026 — in order, fastest first.
Before you debug anything, answer one question: does the image render when you paste the URL into a brand-new test channel? Spin up an empty Slack DM with yourself, an empty LinkedIn draft post, an empty WhatsApp chat with yourself. If the preview works there but is broken on a real post, the issue is cache, not configuration. Skip to fix #4. If it's broken even in a fresh channel, you have a real problem — start at fix #1.
This is the single most common bug and it bites everyone at least once. Your meta tag says <meta property="og:image" content="/images/cover.png">. That looks fine in your editor. But every social scraper on Earth fetches your HTML, reads that string, and tries to GET /images/cover.png against its own domain — not yours. The result: 404, no preview.
The fix is one line: always use an absolute URL with the full https:// prefix. <meta property="og:image" content="https://yourdomain.com/images/cover.png">. If you're using a static-site generator, look for a "site URL" or "base URL" config and make sure your OG image template prepends it.
As of late 2024, every major link-preview fetcher refuses HTTP-only images. LinkedIn, X, Slack, and Apple's iMessage scraper all require https:// end-to-end with a valid cert. Even worse: if your image URL starts as HTTPS but redirects to HTTP — for example, going through an old image CDN that downgrades — most scrapers stop at the redirect and bail.
Test your image URL in isolation:
curl -ILs https://yourdomain.com/path/to/og.png | grep -i "^HTTP\|^location"
If you see anything other than a single HTTP/2 200, fix the redirect chain. Apple's iMessage scraper in particular only follows one redirect; LinkedIn follows up to three but adds latency for each one.
Every platform has both a maximum and a minimum, and almost no one publishes them clearly. Here are the 2026 limits I have personally hit:
| Platform | Max file size | Min dimensions | Hard fail behavior |
|---|---|---|---|
| X / Twitter | 5 MB | 300 × 157 | Falls back to summary card (small square) |
| 5 MB | 1200 × 627 (will downscale) | Shows generic gray placeholder | |
| 8 MB | 200 × 200 | Crops to square or omits entirely | |
| Slack | 5 MB | none documented | Title-only unfurl, no image |
| ~300 KB | 300 × 200 | No preview at all | |
| iMessage | 10 MB | 600 × 314 | Plain blue link, no rich preview |
| Discord | 8 MB | none | Embeds shrunk thumbnail |
WhatsApp is the silent killer here. If you serve a 4 MB hero image, it works everywhere except WhatsApp — and you'll never know unless you test. The simplest fix: export your OG image as a 1200×630 JPG at quality 80, which lands at roughly 150–250 KB and works on every platform on the table.
Every preview-rendering platform caches the result of its first scrape. If your first share happened before the image was deployed (or while it was 404), that broken result gets pinned to the cache for anywhere from 24 hours to 30 days. New shares of the same URL keep returning the old broken preview.
There is no universal cache buster. You have to refresh each platform individually:
?v=2 (or any new query string) to your URL. X treats different query strings as different URLs and re-scrapes.#refresh1, #refresh2, etc. to force a new scrape. The fragment is invisible to your server but distinct to Apple's cache.If you front your site with Cloudflare's Bot Fight Mode, AWS WAF, or any "bot protection" layer, there is a meaningful chance you are blocking the very scrapers you want to allow. The known good list to allowlist in 2026:
facebookexternalhit/1.1 — Facebook + InstagramLinkedInBot/1.0Twitterbot/1.0Slackbot-LinkExpanding 1.0WhatsApp/2.xDiscordbot/2.0Applebot (handles iMessage and Spotlight previews)Test by curling your page with a LinkedIn user agent: curl -A "LinkedInBot/1.0" -I https://yourdomain.com/post. If you get a 403, 503, or a Cloudflare interstitial, your bot protection is the bug.
Scrapers fetch from data centers in the US (mostly Virginia and Oregon) and they don't have your cookies. If your image lives on a private S3 bucket with signed URLs that expire in an hour, or on a CDN that geo-restricts traffic, scrapers get a 403 and your preview silently fails. Move OG images to a public path with no auth, no signed URLs, and no geo rules.
Three common authoring mistakes that all look correct at a glance:
name="og:image" instead of property="og:image". The Open Graph spec uses property; most scrapers tolerate name, but LinkedIn historically does not.og:image tags on the same page. Some scrapers pick the first, some the last, some silently fail. Use the View Source on your live page and search for og:image — you should see exactly one match per platform-specific tag.og:url: not strictly required, but LinkedIn's scraper has been observed to drop the image when og:url is absent and the canonical URL doesn't match the page URL.If you set OG tags client-side via React Helmet, Vue Meta, or any framework that injects them after page load, scrapers that don't execute JavaScript will not see them. As of 2026, only Slackbot and Discordbot reliably execute JS. Facebook, LinkedIn, X, WhatsApp, and Apple all read static HTML only.
If you're on Next.js, render OG tags server-side via the App Router's generateMetadata() or the Pages Router's <Head> component. If you're on Gatsby, use react-helmet-async with SSR. If you're on plain client-side React with no SSR, you cannot make link previews work without a static pre-render — that's the unavoidable cost.
Some image CDNs (Imgix, Cloudinary) generate URLs with timestamp or signature parameters that rotate. If your og:image URL is fresh on every page render, scrapers cache one version and the next render serves a different URL — not technically broken, but the cache stays warm for the wrong asset forever. Pin your OG image to a stable URL with no timestamp.
The TinyTools OG Image Generator outputs a properly sized 1200×630 JPG, under 250 KB, served over HTTPS from a stable URL — every preview platform's happy path by default. No signup, no watermark.
Try it free →After you ship the change, run this loop:
#test1 appended.Five platforms, sixty seconds. If all five render, your link preview is healthy. If one fails, the platform-specific behavior in fix #4 will tell you which cache is still poisoned and how to bust it.
Link previews are an unowned protocol. Open Graph was Facebook's spec in 2010, Twitter Cards added competing tags in 2012, and every platform since has added its own quirks without coordination. There is no W3C body, no shared validator, no cross-platform refresh button. The result: every publisher fights the same nine bugs forever.
The shortcut is to standardize your tooling so the same nine fixes only apply once per site, not per post. Use a generator that outputs the canonical 1200×630 JPG. Set og:image server-side. Pin it to an HTTPS, public, no-redirect, no-query-string URL. Allowlist the known scraper user agents in your WAF. After that, link previews stop being a thing you debug and start being a thing that just works.