You have set og:image, you can see it in the page source, and Facebook still shares the link as a bare text card. The tag being present is not the same as the image being usable: the crawler fetches that image in a separate anonymous request, and it silently drops it if the URL is relative, the file is unreachable, the dimensions are below the floor, or it is serving you a preview it cached before you fixed anything. Each cause has a distinct signature.
What the Error Looks Like
Facebook's Sharing Debugger is unusually explicit about this, and the exact wording tells you which cause you are dealing with:
# Image below the minimum dimensions
Provided og:image is not big enough. Please use an image that's at least
200x200 px. Provided image has a width of 160 px and a height of 90 px.
# Image URL could not be fetched at all
Provided og:image could not be downloaded. This can happen due to several
different reasons such as your server using unsupported content-encoding.
# Image not yet fetched — the first-share race
Provided og:image, https://example.com/og.png, could not be downloaded
because it exceeded the maximum allowed sizes or timed out.
# No image tag found on the page at all
The 'og:image' property should be explicitly provided, even if a value can
be inferred from other tags.LinkedIn's Post Inspector is terser — it simply renders the card without an image. Slack shows the title and description with an empty thumbnail slot.
Cause 1 — The Image URL Is Relative
This is the single most common cause and the easiest to miss, because the page renders the image perfectly in a browser. Scrapers do not resolve a relative path against the page URL:
<!-- Silently ignored -->
<meta property="og:image" content="/static/og/launch.png">
<meta property="og:image" content="//example.com/static/og/launch.png">
<meta property="og:image" content="http://example.com/static/og/launch.png">
<!-- Correct -->
<meta property="og:image" content="https://example.com/static/og/launch.png">The protocol-relative form fails for the same reason, and a plain http:// image on an HTTPS page is dropped as mixed content. The value must be a fully qualified https:// URL.
Cause 2 — The Image Is Too Small
Facebook enforces a hard floor of 200 × 200 pixels and refuses anything below it outright — that is the "not big enough" message above. Between 200 × 200 and 1200 × 630 it accepts the image but upscales it, which is why some cards look soft and washed out.
Export at exactly 1200 × 630 (a 1.91:1 ratio), keep the file under about 1 MB, and declare the dimensions so the platform does not have to download the file before it can lay out the card:
<meta property="og:image" content="https://example.com/static/og/launch.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="A bar chart showing build times dropping to four minutes">og:image:width and og:image:height, the platform must fetch the image before it can size the card. The very first time a URL is shared the scrape is often still in flight, so the card renders imageless — then looks fine to everyone who shares it afterwards. Declaring the dimensions removes that race entirely.Cause 3 — The Crawler Cannot Fetch the Image
The image request is separate from the page request, anonymous, and carries no cookies or referer. Anything that gates it will break the card: authentication, hotlink protection, signed URLs that have expired, a geo-block, a CDN rule, or a robots.txt disallow covering the image directory. Test the image on its own:
# Must return 200 with an image/* content-type
curl -sI https://example.com/static/og/launch.png | head -n 5
# And must still return 200 for the crawler's user agent
curl -so /dev/null -w "%{http_code}\n" \
-A "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)" \
https://example.com/static/og/launch.pngA 403 on the second command and a 200 on the first is the signature of bot protection or hotlink rules. Allowlist facebookexternalhit, LinkedInBot, Twitterbot, Slackbot-LinkExpanding, WhatsApp and Discordbot.
Accept header, pin the og:image URL to a PNG or JPEG explicitly.Cause 4 — The Tag Is Rendered Client-Side
Link scrapers do not execute JavaScript. If a single-page app, a tag manager or a framework helper sets og:image after hydration, the tag exists in DevTools and is completely invisible to the crawler. Confirm what the server actually sends:
curl -sL -A "facebookexternalhit/1.1" https://example.com/your-page \
| grep -i 'og:image'Nothing printed here, while the tag is visible in the browser inspector, means the tag is client-rendered. It must move into the server-rendered HTML — next/head or generateMetadata() in Next.js, the document head in your template engine, or an edge prerender for bot user agents.
Cause 5 — Duplicate or Conflicting Tags
If a base template emits a default og:image and the page template adds its own, the page ships two. Scrapers generally take the first occurrence, so the site-wide default wins and your page-specific image never appears. Count them:
curl -sL https://example.com/your-page | grep -c 'og:image"'
# More than 1? Find which one comes first
curl -sL https://example.com/your-page | grep -n 'og:image'The same applies to a twitter:image that disagrees with og:image — X will use the Twitter-namespaced value, so the card can differ between platforms in a way that looks like a caching bug.
Cause 6 — You Are Looking at a Cached Preview
Facebook caches the scrape result per URL for a day or more. If the link was ever shared before the image was correct, everyone keeps seeing the old imageless card no matter how right the markup is now. This is why so many correct fixes appear to do nothing.
Force a fresh scrape:
- Facebook — Sharing Debugger at
developers.facebook.com/tools/debug/, then Scrape Again. The "Time Scraped" field confirms it refreshed. - LinkedIn — Post Inspector at
www.linkedin.com/post-inspector/; inspecting re-scrapes automatically. - X — Card Validator; caches for around seven days and is the slowest to refresh.
- Slack, WhatsApp, iMessage — no debugger. Append a harmless
?v=2to create a new cache key.
curl or the checker first, then force the refresh.Verify the Fix
Run the URL through the Open Graph Checker. A healthy result shows og:image present and absolute, og:image:width and og:image:height declared, the image served over HTTPS, and a card type of summary_large_image. Then re-scrape in the Sharing Debugger and confirm the image appears in the rendered preview.
# Final sanity check — all four should be true
curl -sL https://example.com/your-page | grep -iE 'og:image'
# 1. exactly one og:image
# 2. value starts with https://
# 3. og:image:width and og:image:height present
# 4. the URL returns 200 image/* to facebookexternalhitFrequently Asked Questions
The Sharing Debugger shows the right image, but shares still have none. Why?
The debugger scrapes fresh; the feed serves what was cached when the post was created. Posts made before the fix keep their original preview permanently — you cannot retroactively update an existing post. Delete and re-share it, or share the URL with a query string appended.
What image size should I use to be safe everywhere?
1200 × 630 pixels, PNG or JPEG, under 1 MB. That satisfies Facebook, LinkedIn, Slack, Discord and X's summary_large_image layout. Platforms that show a square thumbnail centre-crop it, so keep logos and text away from the edges.
Does og:image:alt affect whether the image shows?
No — it is an accessibility attribute, read out by screen readers in place of the card image. It has no bearing on whether the image renders, but it is worth setting: it is the only accessible description a shared link carries.
Can I use an image hosted on a different domain or CDN?
Yes, provided it is publicly fetchable over HTTPS with no hotlink protection and no referer restriction. Cross-origin og:image URLs are completely normal — most sites serve them from a CDN. The requirement is reachability, not same-origin.
Why does the image show on Slack but not on Facebook?
Slack has no minimum dimension enforcement and re-scrapes far more eagerly. So a Slack preview confirms the URL is fetchable but says nothing about whether it clears Facebook's 200 × 200 floor or whether Facebook is still holding a cached result. Check the dimensions, then re-scrape.
My og:image is a dynamically generated URL. Is that a problem?
Only if it is slow or unstable. Generated OG images (serverless renderers, @vercel/og and similar) work fine, but the crawler times out on slow responses, and a signed URL that expires will break the card later. Cache the output aggressively and avoid expiring signatures in og:image.