"og:image relative URL" — Causes & Fix

Your og:image tag is present and points at a real file, but the card has no image. Scrapers never resolve relative paths — the URL has to be absolute.


A relative og:image is silently ignored. Browsers resolve /og/article.png against the current page without complaint, so the path looks correct everywhere you check it — but link scrapers do not do that resolution. They read the content value as a literal URL, fail to fetch it, and render a card with no image. The tag is present, the file exists, and the preview is still text-only. The fix is always the same: emit a full https:// URL.

What the Problem Looks Like

html
<!-- All of these are ignored by scrapers --> <meta property="og:image" content="/og/article.png"> <!-- root-relative --> <meta property="og:image" content="og/article.png"> <!-- path-relative --> <meta property="og:image" content="../images/article.png"> <!-- parent-relative --> <meta property="og:image" content="//example.com/og/a.png"> <!-- protocol-relative --> <!-- This is what works --> <meta property="og:image" content="https://example.com/og/article.png">

Checkers report it in various wordings — "og:image is not an absolute URL", "image URL must be absolute", "could not download image" — and Facebook's Sharing Debugger typically shows the image panel empty with a warning about the provided URL.

Check the og:image value your page serves

Why Scrapers Do Not Resolve It

A browser knows the document's base URL and resolves relative references against it. A link scraper is a different kind of client: it fetches the HTML, extracts content attribute values as strings, and queues them for a separate fetch — often on different infrastructure, minutes later, with no memory of which page the string came from.

By the time the image fetcher runs, the page context is gone. The Open Graph specification requires an absolute URL for exactly this reason, and platforms implement it strictly rather than guessing.

Protocol-relative URLs count as relative//example.com/og/image.png looks absolute and is not — it inherits the scheme from the page, which the image fetcher no longer has. Some scrapers guess https:, others fail outright, which produces the worst symptom of all: an image that renders on one platform and not another. Always write the scheme explicitly.

Why It Happens

1. Copying the pattern from an img tag

In page markup <img src="/og/article.png"> is completely normal, so the same path gets pasted into the meta tag. The two are read by different clients with different rules.

2. A CMS or template emitting a path

Template helpers frequently return a site-root path rather than a full URL. Jekyll's page.image, Hugo's .Params.image and most WordPress theme functions all do this unless you pass them through an absolute-URL filter.

3. Next.js without metadataBase

The App Router lets you write relative image paths in metadata, but it only converts them to absolute URLs if metadataBase is set. Without it, Next logs a warning during build and may emit a relative URL — a very common cause in Next projects.

4. A base tag that only helps browsers

<base href="https://example.com/"> makes relative URLs resolve correctly in a browser, which strongly implies the meta tag is fine. Scrapers do not apply <base> to content attributes.

5. A staging or localhost URL left behind

The inverse problem: an absolute URL pointing at http://localhost:3000 or a staging host. It satisfies the "absolute" requirement and is unreachable from the public internet, producing the same empty card.

How to Diagnose It

bash
# 1. What exact value is served? curl -sL https://example.com/page | grep -i 'og:image' # 2. Does it start with https:// ? curl -sL https://example.com/page \ | grep -oP '(?<=property="og:image" content=")[^"]+' # 3. Is the URL fetchable on its own, with no page context? curl -sI 'https://example.com/og/article.png' | head -1 # 4. And is it actually an image? curl -sI 'https://example.com/og/article.png' | grep -i content-type

Step 3 is the real test — fetch the URL exactly as printed, from a fresh shell, with nothing else supplied. That is precisely what the platform's image fetcher does. If it fails there, it fails for them.

Value servedVerdict
https://example.com/og/a.pngCorrect
http://example.com/og/a.pngAbsolute but insecure — may be blocked as mixed content
//example.com/og/a.pngProtocol-relative — unreliable, treat as broken
/og/a.pngRelative — ignored
og/a.pngRelative — ignored
http://localhost:3000/og/a.pngAbsolute but unreachable

How to Fix It

Next.js — App Router

Set metadataBase and Next resolves relative image paths into absolute URLs for you:

Command
export const metadata = { metadataBase: new URL('https://example.com'), openGraph: { images: [{ url: '/og/article.png', width: 1200, height: 630 }], }, }; // Emits: <meta property="og:image" content="https://example.com/og/article.png">

Drive the origin from an environment variable so preview deployments do not inherit the production host:

Command
metadataBase: new URL( process.env.NEXT_PUBLIC_SITE_URL ?? 'https://example.com' ),

Next.js — Pages Router

Command
const SITE = process.env.NEXT_PUBLIC_SITE_URL ?? 'https://example.com'; <Head> <meta property="og:image" content={`${SITE}/og/${post.slug}.png`} /> </Head>

WordPress

php
// Theme functions return full URLs already — use them directly $image = get_the_post_thumbnail_url($post, 'full'); // absolute $image = $image ?: home_url('/og/default.png'); // absolute fallback printf('<meta property="og:image" content="%s">', esc_url($image));

home_url() and get_the_post_thumbnail_url() both return absolute URLs. Hard-coded paths in a theme are the usual culprit.

Jekyll and Hugo

html
<!-- Jekyll — absolute_url is what makes it work --> <meta property="og:image" content="{{ page.image | default: '/og/default.png' | absolute_url }}"> <!-- Hugo — absURL, and set baseURL in config.toml --> <meta property="og:image" content="{{ .Params.image | default "/og/default.png" | absURL }}">

Both depend on the site URL being configured — url: in Jekyll's _config.yml, baseURL in Hugo's config. If that is missing or set to /, the filters produce relative output and the problem persists.

Shopify

html
<!-- Shopify image filters return protocol-relative URLs starting with // --> <meta property="og:image" content="https:{{ product.featured_image | image_url: width: 1200 }}">

The literal https: prefix is required. Without it you get //cdn.shopify.com/..., which is protocol-relative and unreliable.

Plain HTML

html
<meta property="og:image" content="https://example.com/og/article.png">
og:url has the same requirementog:url must also be absolute. A relative value there does not blank the image, but it breaks deduplication — the platform cannot tell that two shared variants are the same page, and engagement counts split.

Verify the Fix

bash
# 1. The tag is absolute and HTTPS curl -sL https://example.com/page | grep -i 'og:image"' <meta property="og:image" content="https://example.com/og/article.png"> # 2. The URL works standalone curl -sI https://example.com/og/article.png | head -1 HTTP/2 200 # 3. It is a real image of a sensible size curl -sI https://example.com/og/article.png | grep -iE 'content-type|content-length' content-type: image/png content-length: 184223 # 4. Force a re-scrape — the old imageless card is cached

Step 4 matters. The card will keep rendering without an image until the platform re-scrapes, which makes a correct fix look like a failed one. See How to Clear the Social Preview Cache.

Frequently Asked Questions

Why does a relative og:image not work when relative img src does?

A browser resolves relative references against the document it is displaying. A scraper extracts the string and hands it to a separate image fetcher that has no page context — often a different service running minutes later. The specification requires absolute URLs precisely because that resolution cannot happen.

Is a protocol-relative URL acceptable?

No. //example.com/og/image.png depends on inheriting the page's scheme, which the image fetcher does not have. Some platforms guess https: and others fail, so it works inconsistently — which is harder to debug than failing outright.

Can I use http:// instead of https://?

It satisfies the absolute requirement but invites trouble. Platforms serving cards over HTTPS may block an HTTP image as mixed content. Use HTTPS.

Does a base href tag fix this?

No. It affects how browsers resolve relative URLs in the document; scrapers do not apply it to content attributes. It makes the problem harder to spot, because everything looks correct in a browser.

Can the image live on a different domain?

Yes. A CDN or object-storage host is fine as long as the URL is absolute, HTTPS, and publicly reachable with no auth, hotlink protection, or robots.txt rule blocking the crawler.

I fixed the URL but the card still has no image. Why?

Almost certainly the cached scrape from before the fix. Confirm the tag and the standalone fetch both look right, then force a re-scrape in the Sharing Debugger. If it is still empty after that, check the image dimensions against the platform's minimum.

Related