When you paste a link into a chat or a social post, the platform fetches the page once and builds the preview card from the Open Graph tags it finds in that first HTML response. Lovable's documentation, for one, notes that most of the big platforms skip JavaScript at this step (Lovable). So a link with no card, the stock picture of a template, or a blank square in the browser tab nearly always comes down to one of four things: the tags are not in the HTML at all, a script writes them after the page loads, the image points somewhere wrong, or the icon link is missing. This guide goes through each one, shows the fix for common frameworks and gives a check that tells you whether the fix landed. Every fix rests on one rule: the tags have to sit in the HTML the server sends, and the picture they name has to exist.

The Open Graph protocol defines four basic properties every page should carry: og:title, og:type, og:image and og:url, each written as a meta tag in the head (Open Graph protocol). The image property holds the address of a picture that should represent the page. The platform reads those tags and nothing that a script adds later.

That changes how you look. The Elements panel of your browser's developer tools shows the page after scripts have run, so it can display perfect tags that no scraper will ever see. Read the server's own response:

curl -s https://example.com/ | grep -i -E 'og:title|og:image|rel="icon"'

If that prints nothing, the scraper gets nothing either, whatever the browser shows.

Cause one: no Open Graph tags at all

The plainest case is a head with a title and a description and no Open Graph tags. Some platforms then guess a title and a picture from the page, and the guess is rarely the one you wanted. Add the tags by hand in a plain HTML page, or through the framework's metadata in an app.

Next.js has two details here that trip people up, both shown in its own documentation (Next.js):

  • The page title and og:title are separate. In the documentation's example a layout sets both, a page then sets only title, and the output pairs the page's new title tag with the layout's old og:title. Write openGraph.title, openGraph.description and openGraph.images yourself.
  • A page's openGraph replaces the layout's. Metadata from nested segments is merged shallowly, so a page that sets openGraph with only a title loses the image the layout had, while a page that sets no openGraph inherits it whole. Keep shared fields in a variable and spread it into each page's object.

Cause two: tags that only a script writes

A single-page app built with Vite usually has one index.html, and a head library such as a Helmet component rewrites the head once the bundle has run. Visitors see the right title in the tab, while the scraper reads the untouched index.html. Adding another head library changes nothing, because it also runs on the visitor's machine. For a plain Vite app the working fix is to type the tags into index.html itself, which gives every page the same card. Different cards per page need the pages rendered to HTML at build time or on the server.

Next.js draws the same line in its own terms. The metadata object and generateMetadata are available only to Server Components; the documentation explains that this lets Next.js settle the metadata on the server and put it into the very first HTML response (Next.js). A page file that starts with "use client" cannot export them. Keep page.tsx on the server and split the clickable bits off into a separate client component.

Cause three: the picture points somewhere wrong

Two quite different problems end up in this group.

A picture on someone else's host. Templates and page builders often ship with a default preview image hosted on their own domain or on a demo site. Every page of your site then shares a picture of the template. The fix is an image on your own domain, and here is the part that is easy to skip: it counts as a fix only when a file is actually served at the new address. Pointing og:image at your own domain with nothing behind that address replaces a wrong picture with a broken one. Make the file first. Lovable's advice is an image of 1200 by 630 pixels and a few hundred kilobytes, and it warns that WhatsApp drops the preview when the file is bigger (Lovable). In a Vite project a file placed in public/ is copied unchanged into the root of the build output, so public/og.png is served at /og.png (Vite).

A bare path where a full address belongs. A tag like og:image with the value /og.png is a common slip. The Open Graph protocol asks for an image URL, and a path on its own leaves the scraper to guess the host. Next.js handles this inside its metadata object: metadataBase lets the URL fields there use a relative path and completes it to an absolute one, while leaving metadataBase unset and writing a relative path stops the build (Next.js). That is also why a relative og:image on a live Next.js site usually comes from a meta element typed by hand into a layout's JSX. Our reading of the documentation is that such a tag is not a metadata field, so metadataBase never touches it; either rewrite it as a field of the metadata object or give it the complete https address.

Next.js can also make the picture for you. Drop an image named opengraph-image.png (JPG and GIF work too) into a route folder and Next.js writes the image tags for that route; a .tsx file of the same name can draw the picture in code with ImageResponse from next/og. When both exist, the file wins over the metadata object (Next.js).

Things that look broken and are not

Before you change anything, rule out the look-alikes. An image served from your own subdomain or your own CDN is on your host, even when the file name is generic; what matters is whose host it is, not what the file is called. Google takes the same view of icons: the file may sit on a CDN rather than on the site itself (Google Search Central). A working preview that comes from tags set on the server needs no help from a script. And a page that has Open Graph tags but no platform-specific extras is not the reason a card is missing.

Cause four: the blank tab icon

The icon in the tab, and next to your result in Google, comes from a link element with rel="icon" placed in the home page's head. Google's guidelines add a few conditions for search results:

  • the icon must be square; the floor is 8x8 pixels and Google suggests more than 48x48 (Google Search Central);
  • the format must be on Google's list, and SVG is not on it (ICO and PNG are), so an icon that exists only as an SVG may appear in the browser and still not in search;
  • the address should not keep changing, and Googlebot must be allowed to crawl the icon file as well as the home page.

In Next.js, a favicon.ico placed in the root of the app folder makes the framework add an icon link pointing at /favicon.ico, and the documentation notes that code can produce other icons but not the favicon (Next.js). An icon that lives only in public/ is served as a file, but look for its link in the served head before you trust it.

A check that can fail

Run these three against the live site once the fix is deployed, not against the code:

  1. Request the home page with curl, so no JavaScript runs, and look in what comes back for three things: the title tag, the image tag and the icon link.
  2. Request the image address itself. You want status 200 and a Content-Type beginning with image/. If HTML or a not-found reply comes back instead, that means the card will have no picture.
  3. Pick one tag that was right before your change and confirm it is still there. This is the control: a fix that rewrote the whole head can pass the first two checks and lose something else on the way.

Then paste the link into the platform's own link debugger or a private chat to see the card as it is drawn. A separate post on how models stretch titles and descriptions covers the text that goes into these tags, which is a different question from where they live.

When this does not apply

Only two questions are covered here: where the tags live and whether the image exists. The wording of titles or descriptions, the design of the picture and a page that is entirely empty without JavaScript, which needs prerendering before any tag matters, are other topics. Platforms differ in what they read beyond the basic tags, and we did not test each platform's scraper; we relied on the documentation of the protocol, the frameworks and Google, as read on 12 November 2026. One step above is our own reading of the documentation rather than a documented rule: that metadataBase leaves a hand-typed tag alone. Platforms may also keep an older card for a while after you fix the page, which a fresh share or the platform's debugger usually reveals.

What to do next

A review of this kind does a small set of jobs: it finds where the preview tags really come from, tells apart a missing tag, a script-only tag, a picture on someone else's host and a bare path, writes the exact tags for your framework, and makes sure the picture exists before it calls the job done. It also leaves alone what is fine, such as an image on your own CDN or an icon set by the framework's file convention.

We sell a skill for that, Link Preview Fix. You paste the line from the site check, name your framework and host, and add the head your server returns plus the file that produces it. The answer gives each cause its own line with a fixed code, the location, why it happens and what to change, then the tags ready to paste and one line of checks that requests the image, or a single sentence when nothing is wrong. It works only from what you paste and never visits your site, and a framework it does not cover gets an answer of the same form, flagged as not checked. The store is at https://aiskills402.com.

Read this post as Markdown: /blog/link-preview-og-image-favicon-fixes.md · Atom feed.