Why the image matters
The image is the largest part of a link preview. Facebook, LinkedIn, WhatsApp, Discord and Slack all take it from og:image, and X uses og:image when there’s no twitter:image. Without it, most platforms show a text-only card, which is easy to scroll past.
The tags to add
The tags in the 2-minute fix go in the <head> of the page, in the HTML your server sends. What each one needs:
- Use an absolute https URL, with the domain. Open Graph requires absolute URLs, and a relative path such as
/images/share.jpgor a protocol-relative//cdn.example.com/…is ignored by some crawlers. Some platforms also refuse images served over plainhttp://. - Use JPEG or PNG, 1200×630 pixels. WebP is widely supported too. The OG image size cheat sheet has each platform’s exact sizes.
- Declare the size with
og:image:widthandog:image:height. Facebook can then show the image the first time the link is shared, before it has downloaded the file. - Describe the image in
og:image:altfor people using screen readers.
If the page has several og:image tags, platforms generally use the first one, so put your main image first.
Common reasons og:image doesn’t show
These are the problems the checker reports for the image, roughly from most to least common:
- There’s only a twitter:image
- Facebook, LinkedIn and WhatsApp don’t read
twitter:image. Add anog:imagewith the same URL. X reads it too, so you can then removetwitter:imageunless X needs a different picture. - The URL is relative, or points to localhost
- Write the full URL. In Next.js, set
metadataBasein your root layout: without it, relative image paths resolve tohttp://localhost:3000on hosts other than Vercel, and no crawler can reach that. - The value isn’t a URL
- A
data:URI, an empty value or a template variable that never got filled in (such as{{image}}) is ignored. The tag needs a link to a real file. - The URL returns an error or a web page
- Open the image URL in a private window. It should show only the image, without a login, cookie banner or redirect to your home page. Hotlink protection and firewall rules that block unknown bots also stop platforms’ crawlers.
- The image is too slow
- Crawlers give up quickly. The checker flags images that take more than 5 seconds; serve the file from a CDN and keep it small.
- The format isn’t supported
- SVG isn’t shown by Facebook, X, LinkedIn and most others. AVIF and HEIC aren’t shown by several yet. Export a JPEG or PNG. The checker reads the format from the file itself, so a file named
.pngthat is really something else is caught. - The file is too large
- Facebook accepts up to 8 MB and X up to 5 MB, but WhatsApp often drops images over a few hundred KB. A 1200×630 JPEG usually fits in 100 to 300 KB.
- The tags are added by JavaScript
- Link preview crawlers don’t run JavaScript, so tags added in the browser are never seen. See why client-side rendering hides your tags.
The OG image checker fetches your image from our server and reports which of these applies, with its real format, size and file weight.
Code for your framework
These set og:image together with the other tags a preview needs. Replace the example values with your own.
HTML inside <head>
<title>How we cut our build time in half</title>
<meta name="description" content="What we changed in our CI pipeline, and what it saved.">
<link rel="canonical" href="https://example.com/blog/faster-builds">
<!-- Open Graph -->
<meta property="og:title" content="How we cut our build time in half">
<meta property="og:description" content="What we changed in our CI pipeline, and what it saved.">
<meta property="og:url" content="https://example.com/blog/faster-builds">
<meta property="og:type" content="article">
<meta property="og:site_name" content="Example Blog">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://example.com/images/faster-builds.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="A chart of build times dropping from 12 to 6 minutes">
<!-- X (Twitter). X falls back to the og: tags for title, description and image. -->
<meta name="twitter:card" content="summary_large_image">
Next.js (App Router) in page.tsx or layout.tsx
import type { Metadata } from "next";
// app/<route>/page.tsx (or layout.tsx)
export async function generateMetadata(): Promise<Metadata> {
return {
title: "How we cut our build time in half",
description: "What we changed in our CI pipeline, and what it saved.",
alternates: { canonical: "https://example.com/blog/faster-builds" },
openGraph: {
title: "How we cut our build time in half",
description: "What we changed in our CI pipeline, and what it saved.",
url: "https://example.com/blog/faster-builds",
type: "article",
siteName: "Example Blog",
locale: "en_US",
images: [
{
url: "https://example.com/images/faster-builds.jpg",
width: 1200,
height: 630,
alt: "A chart of build times dropping from 12 to 6 minutes",
},
],
},
twitter: {
card: "summary_large_image",
},
};
}
Nuxt in the page component
<script setup lang="ts">
useSeoMeta({
title: "How we cut our build time in half",
description: "What we changed in our CI pipeline, and what it saved.",
ogTitle: "How we cut our build time in half",
ogDescription: "What we changed in our CI pipeline, and what it saved.",
ogUrl: "https://example.com/blog/faster-builds",
ogType: "article",
ogSiteName: "Example Blog",
ogLocale: "en_US",
ogImage: "https://example.com/images/faster-builds.jpg",
ogImageWidth: 1200,
ogImageHeight: 630,
ogImageAlt: "A chart of build times dropping from 12 to 6 minutes",
twitterCard: "summary_large_image",
});
useHead({
link: [{ rel: "canonical", href: "https://example.com/blog/faster-builds" }],
});
</script>
React Router / Remix in the route module
// In a route module (React Router v7 framework mode or Remix v2).
export function meta() {
return [
{ title: "How we cut our build time in half" },
{ name: "description", content: "What we changed in our CI pipeline, and what it saved." },
{ tagName: "link", rel: "canonical", href: "https://example.com/blog/faster-builds" },
{ property: "og:title", content: "How we cut our build time in half" },
{ property: "og:description", content: "What we changed in our CI pipeline, and what it saved." },
{ property: "og:url", content: "https://example.com/blog/faster-builds" },
{ property: "og:type", content: "article" },
{ property: "og:site_name", content: "Example Blog" },
{ property: "og:locale", content: "en_US" },
{ property: "og:image", content: "https://example.com/images/faster-builds.jpg" },
{ property: "og:image:width", content: "1200" },
{ property: "og:image:height", content: "630" },
{ property: "og:image:alt", content: "A chart of build times dropping from 12 to 6 minutes" },
{ name: "twitter:card", content: "summary_large_image" },
];
}
In Next.js you can also put an opengraph-image.jpg file in a route folder, and Next.js adds the tags for you. On WordPress, an SEO plugin such as Yoast SEO usually writes them from the post’s featured image.
After you fix it
Platforms cache previews, so an old card can stay after your fix. Ask Facebook to re-fetch the page in the Facebook Sharing Debugger (opens in a new tab) and LinkedIn in the LinkedIn Post Inspector (opens in a new tab). X has no refresh tool: share the URL with a harmless query string such as ?v=2. If you replaced the image file, give it a new URL as well, because images are cached separately from pages.