OG Image Not Showing When You Share a Link (Fix Checklist)

Slack, X, LinkedIn, or Facebook unfurls your URL with no preview image. The 7 real causes and the exact debug commands and cache-refresh tools.

You paste your URL into Slack and the link unfurls to just a title with no image. Or it showed the image once and never again. Or X shows it but LinkedIn doesn’t. Open Graph is a 2010 protocol, but every platform layers its own rules on top: minimum size, scrape timeout, supported formats, and a server-side cache that can pin a bad result for up to a week.

Fastest fix (covers ~70% of cases): make sure your page has one absolute-HTTPS og:image that returns HTTP 200, then force each platform to re-scrape. Run this first:

curl -s "https://yoursite.com/article" | grep -oE 'og:image" content="[^"]+'

If the URL prints and starts with https://, copy it and check it loads:

curl -sI "https://yoursite.com/og.png" | head -1

If that line is HTTP/2 200, your tags are fine and the problem is almost always a stale platform cache — jump to Force-refresh each platform. If either command comes back empty or non-200, work through the causes below.

Which bucket are you in?

SymptomMost likely causeSection
No image on ANY platformMissing tag, relative/HTTP URL, or non-200 imageCauses 1, 4
Works in browser preview, not when sharedStale cache, or scraper timeout on dynamic imageCauses 3, 5
X shows small card, LinkedIn/FB show largeMissing twitter:card or image too smallCauses 2, 6
Worked yesterday, broke after a deployCache holds old result; image URL or path changedCause 3
Only Slack failsSlack image-proxy cache or unsupported formatCause 7

Common causes

Ordered by how often they’re the culprit.

1. og:image URL is relative or HTTP

Open Graph requires an absolute HTTPS URL. Platforms reject <meta property="og:image" content="/images/cover.png"> and most won’t follow a plain http:// URL either.

How to spot it:

curl -s "https://yoursite.com/article" | grep -oE 'og:image" content="[^"]+'

If the printed URL doesn’t start with https://, that’s your bug.

2. Image is too small or wrong aspect ratio

Each platform has its own floor (as of June 2026):

PlatformMinimumRecommendedAspect
X (summary_large_image)300x1571200x6281.91:1
Facebook200x2001200x6301.91:1
LinkedIn400x4001200x6271.91:1
Slack200x2001200x630any

A single 1200x630 image (1.91:1) satisfies all four. Below 600px wide, X drops the large card and falls back to summary (no image) even when you declared summary_large_image. LinkedIn renders images under 1200px wide blurry or crops them.

How to spot it: open the og:image URL in a browser and check its pixel dimensions, or run curl -s URL | file - to confirm it’s a real image and not an HTML error page.

3. Platform cached an old result (the most common “worked yesterday” case)

X, Facebook, and LinkedIn cache the scrape result server-side. If the page went live before the image was correct, the platform holds the “no image” entry. As of June 2026 the cache windows are roughly: Facebook ~7 days (a first-seen URL can stick for two weeks), LinkedIn ~7 days, X ~7 days, Slack ~30 minutes. A force re-scrape is the only reliable way to skip the window — see Step 3.

How to spot it: run the page through Facebook’s Sharing Debugger. If it shows the old image (or “no image”), the cache is stale.

4. Image returns non-200

The og:image URL returns 404, 403 (auth required), or 500, so no scraper can fetch it.

How to spot it:

curl -sI "https://yoursite.com/og.png" | head -1

It must be HTTP/2 200. A redirect (301/302) sometimes works but not on every platform; serve the final image directly. Also confirm the Content-Type is image/png, image/jpeg, or image/gif — as of June 2026 Facebook’s docs still list only JPG, PNG, and GIF for og:image, and WebP/AVIF/SVG are unreliable across platforms:

curl -sI "https://yoursite.com/og.png" | grep -i content-type

5. Dynamically generated image is slow or times out

If you generate cards on the fly (Satori-based @vercel/og, now built into Next.js App Router as next/og), the first cold request can take a few seconds. Some scrapers give up after about 2 seconds and treat the image as missing.

How to spot it:

curl -o /dev/null -s -w "%{time_total}\n" "https://yoursite.com/api/og?title=test"

If the warm response is consistently above ~2 seconds, scrapers will time out. The fix is aggressive caching so only the first generation is slow (Step 5).

6. twitter:card meta missing or wrong type

For X to show a large image you need both og:image AND <meta name="twitter:card" content="summary_large_image">. Without it, X uses the small card.

How to spot it:

curl -s "https://yoursite.com/article" | grep twitter:card

You want summary_large_image for a large preview. (X falls back to Open Graph tags for title, description, and image, so you do not need duplicate twitter:image tags — but the twitter:card type is required.)

7. CORS / CSP / Slack image-proxy issue

If the image lives on a different domain with strict CORS or a tight Content-Security-Policy, some platforms fail to fetch it. Slack additionally routes images through its own image proxy (Slack-ImgProxy), which caches separately from the metadata — so even after metadata refreshes, an old image can persist.

How to spot it: serve the image from your own origin (or a permissive CDN), not a third-party host with restrictive CORS. For Slack specifically, see the ?v=2 trick in Step 3.

Shortest path to fix

Step 1: View source and verify the tags

curl -s "https://yoursite.com/article" | grep -E 'og:|twitter:'

You want at minimum:

<meta property="og:title" content="..." />
<meta property="og:description" content="..." />
<meta property="og:image" content="https://yoursite.com/og.png" />
<meta property="og:url" content="https://yoursite.com/article" />
<meta name="twitter:card" content="summary_large_image" />

og:image must be absolute HTTPS. If your framework injects tags client-side (a JS-rendered SPA), scrapers won’t run JS — the tags must be in the raw HTML that curl sees.

Step 2: Verify the image URL returns 200

curl -sI "$(curl -s 'https://yoursite.com/article' | grep -oE 'og:image" content="[^"]+' | sed 's/.*content="//')"

You want 200 and Content-Type: image/png (or jpeg/gif).

Step 3: Force-refresh each platform’s cache

The official X validator at cards-dev.twitter.com/validator was deprecated (it no longer renders a preview), so the workflow per platform as of June 2026 is:

  • Facebook / Threads / WhatsApp: developers.facebook.com/tools/debug -> enter URL -> Scrape Again. If the preview doesn’t update, click it 2-3 times; CDN edges can lag up to 24h.
  • LinkedIn: linkedin.com/post-inspector -> enter URL -> Inspect. This refreshes the cache for future posts only; already-shared links keep their old preview until you delete and repost.
  • X (Twitter): no working official validator. Paste the URL into the X composer (don’t post) and the card renders live from the current tags. Third-party previewers also work for a static check.
  • Slack: no public refresh tool and a ~30 min cache. To force a fresh fetch, append a throwaway query param so Slack treats it as a new URL: paste https://yoursite.com/article?v=2. Deleting the unfurled message also clears its cached entry.

Step 4: Set explicit dimensions and alt text

Declaring size lets platforms render the large card without fetching the image first, which also dodges the timeout in Cause 5:

<meta property="og:image" content="https://yoursite.com/og.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Plain-language description of the image" />

Step 5: For dynamic OG images, cache aggressively

If you generate images with @vercel/og / next/og, it already sets cache-control: public, immutable, no-transform, max-age=31536000 in production. If you build the response yourself, set the same header so only the first scrape pays the generation cost:

return new ImageResponse(content, {
  width: 1200,
  height: 630,
  headers: {
    'cache-control': 'public, immutable, no-transform, max-age=31536000',
  },
});

A long max-age means scrapers always hit a cached, fast response. If the card content changes, bust it by changing the URL (a content hash or version param), not by lowering max-age.

Step 6: Confirm it’s actually fixed

Don’t trust a single validator — they sometimes render differently from real posts. After the fix:

  1. Re-run the Facebook Sharing Debugger and confirm a fresh scrape shows the new image and og:image:width/height.
  2. Paste the URL into the X composer and confirm the large card renders.
  3. Run LinkedIn Post Inspector and confirm the preview.
  4. Share the URL once in a private Slack channel (or with ?v=2) and confirm the image unfurls.

If all four show the image, you’re done.

FAQ

Why does my image show in the browser but not when I share the link? Your browser fetches the page normally; social platforms send a separate scraper bot and then cache the result. The usual reasons are a stale cache from before the image was correct (force a re-scrape, Step 3) or a dynamic image that times out on the first cold request (cache it, Step 5).

I fixed the tag but the old image still shows. How long until it updates? Without a manual refresh, expect roughly 7 days on Facebook, LinkedIn, and X, and about 30 minutes on Slack (as of June 2026). Use the per-platform refresh tools in Step 3 to skip the wait. Note that LinkedIn’s refresh only affects new posts; existing posts keep the old preview.

Can I use a WebP, AVIF, or SVG for og:image? Treat them as unsupported. As of June 2026 Facebook’s docs list only JPG, PNG, and GIF, and SVG/AVIF render inconsistently across platforms. Export a PNG or JPEG at 1200x630.

Why does X show a small card while LinkedIn shows a big one? X needs <meta name="twitter:card" content="summary_large_image"> in addition to og:image. Without it X uses the small summary card. It also downgrades to a small/no card if the image is under 600px wide.

My OG tags are correct in dev tools but curl shows nothing. Why? Your tags are likely injected by client-side JavaScript. Social scrapers don’t execute JS, so the tags must be in the server-rendered HTML. Move them to server-side rendering or a static <head>.

Prevention

  • Always use absolute HTTPS URLs in OG tags, and serve the image from your own origin.
  • Always include og:image:width and og:image:height.
  • Default to a 1200x630 PNG or JPEG.
  • After any template change, re-scrape on at least two platforms (Facebook Debugger + LinkedIn Inspector).
  • Cache dynamic OG images with max-age=31536000 and bust by URL change, not by lowering max-age.

Tags: #Troubleshooting #SEO #Debug #OG image