Updated 2026-10-21

og:image not showing in Slack, LinkedIn or X

You share a link and the card has a title and a description but no picture, or a tiny thumbnail instead of a large image. The image in a link preview comes from the page's og:image tag (and twitter:image for X). When it's missing, unreachable or in a format the previewer rejects, the card drops it without telling you why. This guide covers the image specifically; for the full set of tags, see Open Graph tags for every page.

The usual causes

Check what the previewer gets

First the tag, from the raw HTML:

curl -s https://example.com/blog/post | grep -oE '<meta[^>]+(og|twitter):image[^>]*>'

Then the image itself: it should return 200 and an image content type, with no redirect to a login page.

curl -sI https://example.com/og/post.png | grep -iE '^(HTTP|content-type|content-length)'

The free SEO checker does both from a server, the way previewers do, and shows the LinkedIn, Facebook, Slack and X cards it would produce. After a fix, refresh the cached card in LinkedIn Post Inspector and the Facebook Sharing Debugger (Scrape Again).

Astro

Set site in astro.config.mjs to your production URL, then build the absolute URL in your layout with Astro.site:

---
// src/layouts/Base.astro
const { title, description, image = '/og/default.png' } = Astro.props;
const imageURL = new URL(image, Astro.site).href;
---
<head>
  <meta property="og:title" content={title} />
  <meta property="og:description" content={description} />
  <meta property="og:image" content={imageURL} />
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:image" content={imageURL} />
</head>

Without site, Astro.site is undefined and new URL throws on a relative path, which is better than shipping a broken tag. Put shared images in public/ so their URL doesn't change between builds; an image imported through astro:assets gets a hashed file name, so pass its .src to new URL rather than writing the path by hand.

Starlight sets og:title, og:description and twitter:card but no image by default. Add one for every page with the head option:

starlight({
  title: 'My Docs',
  head: [
    { tag: 'meta', attrs: { property: 'og:image', content: 'https://docs.example.com/og.png' } },
  ],
})

Next.js (App Router)

Set metadataBase once in the root layout. Relative image paths in metadata are then resolved against it; without it, a relative path fails the build (Next.js: metadataBase).

// app/layout.tsx
export const metadata = {
  metadataBase: new URL('https://example.com'),
  openGraph: { images: '/og/default.png' },
}

The pitfall: metadata from layouts and pages is merged shallowly. A page that sets any openGraph field replaces the whole openGraph object from the layout, image included:

// app/blog/[slug]/page.tsx: this page loses the layout's og:image
export const metadata = {
  openGraph: { title: 'My post' },
}

Repeat the image in the page's openGraph, spread a shared object into it, or use the file convention: an opengraph-image.png (or .jpg) in a route folder applies to that route and the routes below it.

Hugo

Use Hugo's embedded templates in your <head>:

{{ partial "opengraph.html" . }}
{{ partial "twitter_cards.html" . }}

They take the image from the page's images front matter, then from a page resource whose name contains feature, cover or thumbnail, then from params.images in your site configuration (Hugo: embedded templates). Give the site a default:

# hugo.toml
baseURL = 'https://example.com/'
[params]
  images = ['og/default.png']
---
title: My post
images: ['og/my-post.png']
---

Absolute URLs are built from baseURL, so check it's the production address in the environment that deploys. Older themes call the templates as {{ template "_internal/opengraph.html" . }}; recent Hugo versions use the partial form shown above.

If the tag is right and the card still has no image

  1. Open the image URL in a private window. If you see a login page, so does the previewer.
  2. Check your host's bot protection and any password protection on the domain you shared.
  3. Look for two og:image tags with different values, for example one from your theme and one from a plugin. Previewers usually take the first.
  4. Ask LinkedIn and Facebook to fetch the page again. Slack and X also cache cards, so a link shared before the fix may keep its old preview.

Automating it with Headlark

Headlark's build step writes the link preview tags into each page's HTML after your build. For the image it's deliberately conservative:

Try it

The free plan covers one site and 25 pages. Add your site, copy its secret key, and your next build ships with SEO.