Why Open Graph Tags Don’t Update When Shared

You set per-route Open Graph tags in your single-page app, but when someone shares the link the preview shows the wrong title and image — or nothing. The reason is simple and absolute: social scrapers fetch the raw HTML and do not run JavaScript, so any og: tag your client injects after load does not exist for them. They see only the shell’s default tags. Fixing this means putting Open Graph data in the HTTP response, which is the scraper-facing half of dynamic Open Graph and Twitter card injection.

Step-by-step fix

  1. Confirm scrapers see no OG tags. Fetch the URL the way a scraper does — without JavaScript.

    curl -sL https://example.com/blog/post | grep 'og:'
    # ❌ Returns only the shell defaults → client-injected tags are invisible to scrapers
  2. Render per-route OG tags server-side or in prerendered HTML. The tags must be in the response body, not added later by the client.

    <!-- ✅ In the server response for /blog/post -->
    <meta property="og:title" content="The Real Post Title">
    <meta property="og:description" content="A specific summary of this post.">
    <meta property="og:image" content="https://example.com/og/post.png">
    <meta property="og:url" content="https://example.com/blog/post">
  3. Generate dynamic preview images at request or build time so each route has a real image — see dynamic OG image generation for blog SPAs.

  4. Re-scrape to refresh the cache. Platforms cache previews; use their debugger to force a fresh scrape after deploying.

    ❌ Share the link again and hope → platform serves the stale cached preview
    ✅ Run the platform's sharing debugger on the URL → re-scrapes and refreshes

Validation

  • curl … | grep 'og:' returns the route’s real OG tags without JavaScript.
  • Platform sharing debuggers show the correct title, description, and image.
  • A fresh share in a private channel renders the right preview.
  • GSC URL Inspection rendered HTML matches the OG values for consistency.

Reference

<!-- Per-route OG + Twitter tags, server-rendered so scrapers see them -->
<meta property="og:type" content="article">
<meta property="og:title" content="{{ post.title }}">
<meta property="og:description" content="{{ post.summary }}">
<meta property="og:image" content="{{ post.ogImage }}">
<meta property="og:url" content="{{ post.url }}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ post.title }}">
<meta name="twitter:image" content="{{ post.ogImage }}">

Frequently Asked Questions

Why is my social share preview blank or wrong? Social scrapers like Facebook’s and Slack’s fetch the raw HTML and do not execute JavaScript, so Open Graph tags injected by your SPA after load are invisible to them. They see only the shell’s default tags. The fix is to render og: tags server-side or in prerendered HTML.

Why does the preview stay wrong after I fix the tags? Platforms cache the scraped preview. After deploying correct server-rendered tags, use the platform’s sharing debugger to re-scrape the URL and refresh its cached image, title, and description.

← Back to Dynamic Open Graph and Twitter Card Injection