Fix: Broken Open Graph tags
Open Graph controls how your page renders when a user drops your link into Slack, Twitter, LinkedIn, or iMessage. When these tags are incomplete or missing, social platforms default to scraping raw HTML. The result is a broken preview: a missing image, an auto-generated title, or a truncated description. Social sharing is a primary traffic acquisition channel, and broken previews drastically reduce click-through rates.
Indxel validates Open Graph completeness using three specific rules: og-image (weight: 8/100), og-title (weight: 4/100), and og-description (weight: 4/100). Combined, these represent a 16/100 impact on your total Indxel score. Failing the og-image rule alone will trigger a critical severity error and block your CI pipeline by default.
How do you detect broken Open Graph tags?
Run npx indxel check to scan your routes and output Open Graph validation errors directly in your terminal.
The Indxel CLI parses your rendered HTML and validates the presence, length, and HTTP status of your Open Graph tags. It outputs ESLint-style warnings and errors, pointing exactly to the failing route.
$ npx indxel check
Route: /blog/how-to-configure-caching
✖ [og-image] Missing og:image tag. Previews will render as text-only.
✖ [og-description] og:description is missing.
⚠ [og-title] og:title is present but exceeds 60 characters (72).
Route: /pricing
✖ [og-image] og:image URL returns 404 Not Found (https://example.com/missing.png).
Found 3 errors, 1 warning.
Score: 84/100. CI check failed.The og-image rule is weighted at 8/100 and classified as a critical severity issue. Indxel actually pings the image URL during the check. If the tag exists but the image returns a 404, the rule still fails.
How do you fix broken Open Graph metadata?
Define base Open Graph properties in your root layout and override them on specific pages using absolute image URLs.
The correct approach depends on your framework. In modern Next.js applications, you should establish a fallback configuration in your root layout, then provide specific overrides for individual routes.
Next.js App Router
Next.js App Router uses the built-in Metadata API.
The most common mistake is failing to define metadataBase. Without metadataBase, Next.js generates relative URLs for your og:image. Social platforms like Twitter and Slack do not know your domain name, so a relative URL (/images/og.png) results in a broken image.
Bad: Missing metadata, or missing metadataBase causing relative URL failures.
// app/layout.tsx
import { Metadata } from 'next';
// ✖ BAD: No metadataBase, no openGraph fallback.
export const metadata: Metadata = {
title: 'My SaaS Tool',
description: 'The best tool for developers.',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}Good: Complete Open Graph fallback with metadataBase defined.
// app/layout.tsx
import { Metadata } from 'next';
// ✔ GOOD: metadataBase ensures absolute URLs. Complete OG fallback.
export const metadata: Metadata = {
metadataBase: new URL('https://yourdomain.com'),
title: {
default: 'Your SaaS Tool',
template: '%s | Your SaaS Tool',
},
description: 'The developer-first infrastructure tool.',
openGraph: {
title: 'Your SaaS Tool',
description: 'The developer-first infrastructure tool.',
url: 'https://yourdomain.com',
siteName: 'Your SaaS Tool',
images: [
{
url: '/images/og-default.png', // Resolves to https://yourdomain.com/images/og-default.png
width: 1200,
height: 630,
alt: 'Your SaaS Tool preview image',
},
],
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: 'Your SaaS Tool',
description: 'The developer-first infrastructure tool.',
images: ['/images/og-default.png'],
},
};For individual pages, override the necessary fields. Next.js deeply merges the openGraph object.
// app/blog/[slug]/page.tsx
import { Metadata } from 'next';
export async function generateMetadata({ params }): Promise<Metadata> {
const post = await fetchPost(params.slug);
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
type: 'article',
url: `/blog/${post.slug}`,
images: [
{
url: post.coverImage, // Must be absolute, or resolved via metadataBase
width: 1200,
height: 630,
alt: post.title,
},
],
},
};
}Next.js Pages Router
If you are maintaining a Next.js Pages Router application, you must inject <meta> tags directly into the next/head component.
// pages/index.tsx
import Head from 'next/head';
export default function Home() {
return (
<>
<Head>
<title>Your SaaS Tool</title>
<meta name="description" content="The developer-first infrastructure tool." />
{/* Open Graph */}
<meta property="og:title" content="Your SaaS Tool" />
<meta property="og:description" content="The developer-first infrastructure tool." />
<meta property="og:url" content="https://yourdomain.com/" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://yourdomain.com/images/og-default.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
{/* Twitter */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Your SaaS Tool" />
<meta name="twitter:description" content="The developer-first infrastructure tool." />
<meta name="twitter:image" content="https://yourdomain.com/images/og-default.png" />
</Head>
<main>Content</main>
</>
);
}The Indxel SDK Approach
To enforce Open Graph completeness at compile time, use the Indxel SDK to wrap your metadata generation. The defineSEO function enforces strict TypeScript checks, failing your local build if og:image or og:description are omitted.
// lib/seo.ts
import { defineSEO } from '@indxel/sdk';
export const getPageSEO = defineSEO({
siteName: 'Your SaaS Tool',
baseUrl: 'https://yourdomain.com',
defaultImage: '/images/og-default.png',
twitterHandle: '@yoursaas',
});
// app/pricing/page.tsx
import { getPageSEO } from '@/lib/seo';
// TypeScript throws an error if 'title' or 'description' are missing.
// Open Graph and Twitter card properties are automatically generated and synced.
export const metadata = getPageSEO({
title: 'Pricing',
description: 'Simple, transparent pricing for developers.',
// Uses defaultImage automatically
});How do you prevent broken OG tags in CI?
Add the Indxel CLI to your GitHub Actions workflow or Vercel build command to fail the build if OG tags are missing.
Catching broken Open Graph tags in production is too late. You need an automated guard rail. Add npx indxel check --ci to your pipeline. By default, Indxel fails the CI run if the overall score drops below 90, or if any critical rules (og-image) fail.
Vercel Build Command: Navigate to Settings > General > Build & Development Settings and override the build command:
npx indxel check --ci && next buildGitHub Actions:
To avoid scanning the entire site on every commit, use the --diff flag. This only runs the Open Graph validation against files changed in the specific Pull Request.
name: SEO Validation
on: [pull_request]
jobs:
indxel-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run Indxel SEO Check
run: npx indxel check --ci --diffFor dynamic routes (like /blog/[slug]), static code analysis isn't enough. Run npx indxel crawl https://staging.yourdomain.com --ci against your staging URL to validate the rendered HTML output of dynamic pages before promoting to production.
What are the most common Open Graph edge cases?
Relative image URLs, unsupported image formats, and unhandled errors in dynamic metadata generation are the most frequent causes of broken social previews.
Even with a proper layout configuration, developers frequently encounter these three failure modes:
1. Dynamic Metadata Failures
When using generateMetadata to fetch database records for Open Graph tags, network failures cause Next.js to throw a 500 error or return empty metadata. Wrap your metadata fetch in a try/catch block and return a fallback object.
export async function generateMetadata({ params }): Promise<Metadata> {
try {
const post = await fetchPost(params.slug);
if (!post) throw new Error('Post not found');
return {
title: post.title,
openGraph: { title: post.title, images: [post.image] }
};
} catch (error) {
// Return safe fallback if database goes down
return {
title: 'Blog | Your SaaS',
openGraph: { title: 'Blog | Your SaaS', images: ['/images/og-default.png'] }
};
}
}2. Unsupported Image Formats
WebP and AVIF offer superior compression, but Slack and LinkedIn frequently fail to parse them in og:image tags. Always use standard PNG or JPEG files for Open Graph images.
3. Trailing Slashes in Canonical URLs
Facebook's scraper treats https://domain.com/pricing and https://domain.com/pricing/ as two entirely different entities, splitting your like/share counts. Ensure your og:url exactly matches the <link rel="canonical"> tag.
Which related Indxel rules should you check?
Review the specific missing-og-image, missing-og-title, and missing-twitter-card rules to ensure complete social metadata coverage.
missing-og-image: Focuses specifically on the presence of the image node.missing-og-title: Validates that the Open Graph title matches or intentionally diverges from the<title>tag.missing-og-description: Checks that the OG description provides sufficient context (typically 65-200 characters).missing-twitter-card: Ensurestwitter:cardandtwitter:creatornodes exist for optimal Twitter rendering.
FAQ
Do I need both Open Graph and Twitter card tags?
Yes. Twitter falls back to Open Graph tags if twitter: tags are missing, but for the best parsing reliability you should set both explicitly. Indxel checks both independently to guarantee cross-platform compatibility.
Why does my OG image not update after I change it?
Social platforms cache Open Graph data aggressively for up to 30 days. Use Facebook's Sharing Debugger and Twitter's Card Validator to force a manual cache refresh. For Slack, append a dummy query parameter (?v=2) to the URL to bust the local cache.
Can I use SVG or WebP for my og:image?
No. Use PNG or JPEG. While modern browsers support SVG and WebP natively, the backend scrapers that power iMessage, Slack, and LinkedIn previews routinely fail to render them.
What is the correct size for an Open Graph image?
The optimal resolution is 1200x630 pixels. This provides a 1.91:1 aspect ratio, which is the standard layout for large summary cards across Twitter, Facebook, and LinkedIn. Keep the file size under 5MB to ensure scrapers do not time out while downloading it.
Frequently asked questions
Do I need both Open Graph and Twitter card tags?
Twitter falls back to OG tags if twitter: tags are missing, but for best results set both. Some platforms only read OG, others prefer Twitter cards. Indxel checks both independently.
Why does my OG image not update after I change it?
Social platforms cache OG data aggressively. Use Facebook's Sharing Debugger and Twitter's Card Validator to force a refresh. For Slack, you may need to wait or use a URL parameter to bust the cache.