Back to blog
SEOOpenGraphScreenshots

Why Your Site Needs Custom Preview Images for Search Results

Learn how to generate website previews for search results that actually show up in Google, social shares, and link unfurls — with working code examples.

2026-06-156 min read

When a user sees your link in Google's discover feed, a Slack channel, or an iMessage thread, the preview image often decides whether they click. Yet most sites either ship no preview at all or rely on a single hand-made image that gets recycled across thousands of URLs. Generating fresh, per-page previews used to be a chore involving headless Chrome, queue workers, and storage. It doesn't have to be.

This post walks through how to generate website previews for search results and social cards programmatically, what the major platforms actually look at, and how to wire it all together without running your own browser fleet.

What "search result previews" actually means in 2025

The term covers a few overlapping surfaces, and each has slightly different rules:

  • Google Search — uses the og:image tag for rich results and Discover. Recommended size is at least 1200×630 pixels and under 8MB.
  • Google Discover — requires max-image-preview:large in your robots meta tag, otherwise it shows a thumbnail.
  • Twitter / X — reads twitter:image, falls back to og:image. Cards are either summary (square) or summary_large_image (1.91:1).
  • Slack, Discord, LinkedIn, iMessage — all parse Open Graph tags and cache them aggressively.
  • Bing — uses og:image and occasionally generates its own screenshot if no image is provided.

The practical implication: if you serve a unique, accurate og:image per URL, you cover roughly 95% of the surfaces where your link will appear.

Why static OG images fail at scale

A typical SaaS or content site has three buckets of pages:

  1. Marketing pages — small set, change rarely. Hand-designed images work fine here.
  2. Dynamic content — blog posts, docs pages, user profiles, dashboards. New ones appear daily.
  3. User-generated content — listings, threads, comments. Thousands or millions of URLs.

For buckets two and three, designing images manually is impossible. Most teams fall back to a single generic image, which kills click-through rates because every link in a Slack thread looks identical.

The two production approaches

There are essentially two ways to generate previews automatically:

  • Template rendering — generate an SVG or HTML page styled like a card (title + author + logo), then rasterize it. Tools like Vercel's @vercel/og or Satori do this.
  • Live screenshots — take an actual screenshot of the rendered page. This is more honest because the preview matches what visitors see.

Template rendering looks polished but requires design work per page type. Live screenshots scale to any URL on your site with zero per-template effort, which is why they're often the better starting point.

Generating previews with a screenshot API

Running headless Chrome yourself means dealing with memory leaks, font loading, queue management, and CDN caching. A screenshot API like PxShot handles the browser side so you can focus on where to put the image.

A basic request looks like this:

GET https://api.pxshot.dev/v1/screenshot
  ?url=https://yoursite.com/blog/post-slug
  &width=1200
  &height=630
  &format=png

The response is the binary image. To turn this into a per-page OG image, you have two patterns:

Pattern 1: Render on demand, cache at the edge

Create an endpoint like /og/[slug].png that:

  1. Receives the request from a crawler (Googlebot, Slackbot, etc.).
  2. Calls the screenshot API with the canonical URL of the page.
  3. Streams the result back with a long Cache-Control header.
  4. Lets your CDN cache the image so repeat requests cost nothing.

This is the simplest setup. The first crawler hit pays the screenshot cost; everyone else hits the cache.

Pattern 2: Pre-generate on publish

If you have a CMS or content pipeline, generate the screenshot when content is published or updated:

  1. Webhook fires on publish.
  2. Worker calls the screenshot API.
  3. Image gets uploaded to S3 or R2.
  4. Your page's og:image tag points directly at the stored image.

This pattern is cheaper at scale and gives you predictable load. It's the right choice if you have more than a few thousand pages.

Wiring the meta tags correctly

Once you have an image URL, your page head needs the right tags. The minimum viable set:

<meta property="og:title" content="Post title here">
<meta property="og:description" content="One-line summary.">
<meta property="og:image" content="https://cdn.yoursite.com/og/post-slug.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://cdn.yoursite.com/og/post-slug.png">
<meta name="robots" content="max-image-preview:large">

That last line is the one most people miss. Without max-image-preview:large, Google will show a thumbnail in Discover even if your og:image is perfect.

Things to verify before shipping

Before pushing this live across your whole site, check these:

  • Image dimensions — 1200×630 is the safe default. Going larger wastes bandwidth; smaller looks blurry on Retina displays.
  • File size — keep under 1MB if possible. WebP with a PNG fallback works, though most platforms now accept WebP directly.
  • Absolute URLsog:image must be an absolute URL with protocol. Relative paths get silently ignored by most crawlers.
  • Cache busting — when you update content, the image URL needs to change (or you need to purge Slack and Facebook's caches via their debugger tools).
  • Crawler access — make sure robots.txt doesn't block your OG image path. This trips up more sites than you'd expect.

Testing what crawlers actually see

Don't trust that it works because it loads in your browser. Use:

  • Facebook's Sharing Debugger
  • Twitter's Card Validator (now inside the X developer portal)
  • LinkedIn's Post Inspector
  • Google's Rich Results Test for structured data sanity

Each of these will show you the cached version and let you force a refresh. If you change your image generation logic, you'll need to re-scrape every URL you care about — or wait for the cache TTL, which can be a week or more.

A note on cost and rate limits

If you're generating previews for a 50,000-page site, your numbers look like this with the on-demand pattern: maybe 5,000 unique URLs get crawled per month, so you need around 5,000 screenshots and store them on a CDN. With PxShot's free tier you can prototype the whole thing without a credit card, and paid plans start cheap enough that the math works even for high-traffic content sites.

Spin up an account at pxshot.dev and you can have working per-page previews in production by the end of the afternoon.