An og:image tag that's technically present and still doesn't render is one of the more frustrating small bugs in web development, because the tag looks correct in the page source and the failure only shows up on a platform you don't control, days after you shipped it. Almost every case traces back to one of a small number of causes, and none of them are "the tag is missing."
The Minimum Viable og:image Setup
<meta property="og:title" content="How Image Compression Actually Works" />
<meta property="og:description" content="A developer-level breakdown of lossy vs lossless compression." />
<meta property="og:image" content="https://example.com/og/compression-guide.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:type" content="image/png" />
<meta property="og:url" content="https://example.com/blog/compression-guide" />
<meta property="og:type" content="article" />
The Open Graph protocol only formally requires og:title, og:type, og:image, and og:url, but og:image:width/og:image:height are effectively required in practice — without them, some crawlers fetch the image before deciding whether to render a large card, adding latency and occasionally causing the preview to fall back to nothing.
The Failures That Actually Cause This
1. Relative image URLs
<!-- Broken: most crawlers won't resolve this correctly -->
<meta property="og:image" content="/images/og/post.png" />
<!-- Correct -->
<meta property="og:image" content="https://example.com/images/og/post.png" />
The spec is unambiguous that og:image should be an absolute URL, but relative paths are an easy mistake to ship, especially when the same templating logic generates both regular <img> tags (where relative paths are fine) and Open Graph meta tags (where they aren't reliably resolved).
2. Image below platform minimums
Recommendations vary slightly by platform, but a safe target that satisfies nearly all of them is 1200×630px, roughly a 1.91:1 aspect ratio. Images that are too small get rejected or downgraded to a small thumbnail instead of the large card format. If you're generating og:images dynamically, resize consistently rather than shipping whatever dimensions the source asset happens to have — the Image Resizer can standardize a batch of source images to the target dimensions before you wire them into templates.
3. Tag injected client-side, after the crawler already left
Social platform crawlers generally do not execute JavaScript the way a browser does — they read the server-rendered (or statically generated) HTML response. If og:image is set via a client-side useEffect or injected by a JS-only tag manager, the crawler often sees a page with no image tag at all, because it never runs the script that would add it. In frameworks with server rendering or static generation (Next.js's Metadata API, for example), set Open Graph tags at the point where HTML is generated on the server, not after hydration.
4. Stale cached previews
Social platforms cache the scraped Open Graph data per URL, sometimes for a long time. If you fix a broken tag and the preview still looks wrong, the fix probably worked and you're looking at a cached result. Force a re-scrape with the platform's own tool rather than waiting:
- Facebook / Instagram: Sharing Debugger
- LinkedIn: Post Inspector
- Twitter/X doesn't publish an equivalent public tool in the same form; changing the URL slightly (a harmless query parameter) or waiting out the cache window are the practical workarounds there.
5. Server blocking the crawler's user agent
Bot-protection rules, aggressive rate limiting, or a CDN configuration that blocks unfamiliar user agents can silently return an error page to the social crawler while your browser sees the real page fine. Check server logs for the crawler's requests (facebookexternalhit, LinkedInBot, etc.) — if they're getting a non-200 response, that's the actual bug, and it will look identical to "the tag isn't working" from the outside.
Don't Forget Twitter/X Card Tags
Open Graph tags cover Facebook, LinkedIn, Slack, Discord, and most other platforms, but X (formerly Twitter) historically reads its own twitter:* meta tags first and only falls back to Open Graph tags for fields it's missing. If you only ship og:image, X-based previews can still work via that fallback, but it's not guaranteed for every field, and it's cheap to be explicit:
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="How Image Compression Actually Works" />
<meta name="twitter:description" content="A developer-level breakdown of lossy vs lossless compression." />
<meta name="twitter:image" content="https://example.com/og/compression-guide.png" />
summary_large_image is the card type that renders a full-width image like the Open Graph preview; the default summary card renders a small square thumbnail next to the text instead, which is rarely what you want for a blog post or product page. Twitter Card markup is documented on X's developer site, and it's worth treating as a second, small checklist item alongside Open Graph rather than assuming one implies the other.
Image Format and File Considerations
Not every image format is treated equally by every crawler. JPEG and PNG are universally safe. WebP is supported by most modern crawlers but has historically had inconsistent support on older or less-maintained scrapers, so if a platform's preview silently fails only for WebP images while JPEG works fine on the same URL, that's a known class of bug rather than a fluke. Animated formats (GIF, animated WebP) generally get flattened to a static first frame rather than animating in the preview card — don't rely on motion to carry information in an og:image. SVG is not a safe choice for og:image across platforms; raster formats are the reliable default. If you're unsure whether a source image needs converting before it goes into a template, the Image Resizer also handles format conversion alongside resizing, so you can standardize dimensions and output format in the same pass.
Debugging Workflow
- View the raw server-rendered HTML, not the browser DOM after JS runs —
curlthe URL or use "view source," not devtools' Elements panel, since that reflects the post-hydration DOM. - Confirm the image URL is absolute and returns 200 with a correct content-type when fetched directly.
- Preview the tags before pushing to production — the OG Preview tool renders how title, description, and image will actually appear across platforms without needing a real crawl.
- Force a re-scrape with the platform debugger after deploying a fix, rather than assuming a fresh crawl will happen soon.
- Check server logs for the crawler's user agent if the debugger itself reports a fetch failure.
Decision Point: Static vs Dynamically Generated og:images
If your content is largely static (a handful of marketing pages), a hand-picked 1200×630 image per page is simplest and gives you full art-direction control. If you're generating pages programmatically (blog posts, product pages, user-generated content), invest in an automated og:image generation pipeline (server-rendered image generation, or a templated design with the title/data overlaid) rather than falling back to one generic site-wide image — a generic fallback image on every shared URL is a common reason click-through from social shares underperforms, since every preview looks identical regardless of what's being shared.
Once the tags are in place, validate them with the OG Preview tool before your next deploy, and re-check after any platform's debugger flags a cache issue — that combination catches the vast majority of "og:image isn't showing up" reports before they reach a real audience.