There is a special kind of frustration reserved for the image that works. You open https://yoursite.com/og/launch.png in a tab, it loads instantly, it looks great. You paste the page into LinkedIn and get a headline, a domain, and no picture.
Most write-ups about broken link previews stop at "make sure og:image is set". This post is about the next layer: the tag is set, the file exists, and the crawler still can't use it. Almost every case I've debugged comes down to one of six URL-level problems. Each one has a quick way to prove it from the terminal.
First, stop testing with your browser
Your browser is the worst possible witness here. It has your cookies, it follows any redirect, it resolves relative paths against whatever page you're on, and it happily renders an image even when the server lies about what it is.
A preview crawler has none of that context. It gets one HTML response, pulls a string out of a <meta> tag, and makes a single cold request for that string. So test the same way:
# 1. What does the HTML actually say?
curl -s https://yoursite.com/blog/launch | grep -i 'og:image'
# 2. What happens when a stranger requests that exact value?
curl -sI -A "facebookexternalhit/1.1" "https://yoursite.com/og/launch.png"
The second command is the one that matters. You want a 200, an image/* content type, and a reasonable content-length. Anything else is your bug.
Problem 1: the value is a relative path
<!-- Looks fine, often fails -->
<meta property="og:image" content="/og/launch.png" />
The Open Graph protocol expects a full URL. Some crawlers resolve relative paths against the page URL, some don't, and some resolve them against the URL before redirects, which is a different host than you think. Don't make the crawler guess:
<meta property="og:image" content="https://yoursite.com/og/launch.png" />
In frameworks, this usually comes from a missing base URL setting. In Next.js that's metadataBase; in Astro it's site in the config; in Hugo it's baseURL. If the base is unset or set to localhost in production, you ship relative or localhost URLs without noticing, because your own browser on your own site "fixes" them.
A quick grep catches it in CI:
curl -s https://yoursite.com/blog/launch \
| grep -oE 'og:image" content="[^"]+' \
| grep -vE 'content="https://' && echo "og:image is not absolute"
Problem 2: the image URL redirects somewhere unfriendly
Redirects are not automatically fatal, but every hop is a chance to break. Common chains I've seen:
-
http://→https://→www.→ CDN signed URL - an image path that 302s to a login page for anyone without a session
- an old asset path that redirects to the homepage (so the crawler gets HTML)
Follow the chain and look at where it lands:
curl -sIL -A "LinkedInBot/1.0" "https://yoursite.com/og/launch.png" \
| grep -iE '^(HTTP|location|content-type)'
If you see more than one hop, publish the final URL directly in the tag. If the last hop is text/html, the crawler received a web page, not a picture.
Problem 3: the server lies about the content type
This one is sneaky because browsers sniff content and forgive you. Crawlers often don't. Typical causes:
- A storage bucket serving
.pngfiles asapplication/octet-stream. - A framework catch-all route that returns your SPA shell with a
200for any path, including a mistyped image path. The "image" is yourindex.html. - A file uploaded with a
.jpgextension that is actually WebP or AVIF.
curl -s "https://yoursite.com/og/launch.png" -o /tmp/og && file /tmp/og
If file says HTML document, you found the problem. Also, stick to JPG, PNG, or WebP for preview images. AVIF and SVG support across preview crawlers is uneven, and an SVG with no explicit pixel dimensions gives a crawler nothing to size the card with.
Problem 4: the URL expires
Signed URLs from S3, GCS, Cloudinary or an image CDN often carry an expiry. That's great for private files and terrible for og:image. The tag gets scraped once, maybe again a week later, and by then the signature is dead.
Symptoms: the preview works on day one and silently disappears for later shares. If your og:image contains X-Amz-Expires, Expires=, token= or similar, give the image a stable public URL instead.
Problem 5: the file is too big or too small
Two opposite failures:
- Too big. A 6 MB PNG straight out of a design tool can time out the crawler's fetch. Some apps are stricter than others about file size, and chat apps in particular tend to give up quickly. Export a compressed JPG or WebP around 1200×630; it usually lands well under 300 KB.
- Too small. A 150×150 logo is technically an image, but most platforms won't use it for a large card, or they'll fall back to a tiny thumbnail.
# Size in bytes and pixel dimensions (needs ImageMagick)
curl -s "https://yoursite.com/og/launch.png" -o /tmp/og.png \
&& ls -l /tmp/og.png && identify /tmp/og.png
Problem 6: same URL, new image
You fixed the picture, uploaded it to the same path, and the old one keeps showing up. That's not a crawler failure; it's a cache doing its job. Platforms store the image they scraped, and many chat apps have no refresh button at all.
Version the URL whenever the image changes:
<meta property="og:image" content="https://yoursite.com/og/launch.png?v=3" />
Or bake a content hash into the filename, which most build tools already do for other assets.
Checking all six in one pass
Running these curl commands for every page gets old. I built a free tool for exactly this step: the OG Scope OG image checker. You paste a public URL and it shows the og:image value exactly as published next to the resolved absolute URL it actually requested, whether a real image came back, and the pixel size when the file format exposes it. It warns when the URL isn't absolute, when the file can't be loaded, when a side is under 200 px, or when it's below the usual 1200×630 size.
A couple of honest limits: it fetches the public HTML server-side and doesn't run your JavaScript (neither do most social crawlers, which is the point), and it can't tell you what Facebook or LinkedIn currently has cached. For stale caches, use the platform's own tools: Facebook's Sharing Debugger and LinkedIn's Post Inspector.
A short checklist to keep
Before you share a link that matters:
-
og:imageis an absolutehttps://URL. - That exact URL returns
200with animage/jpeg,image/pngorimage/webpcontent type, with no login and no long redirect chain. - The URL doesn't expire.
- The file is roughly 1200×630 and comfortably under a few hundred KB.
- When the image changes, the URL changes too.
Get those five right and the remaining preview bugs are usually about caching or text, which are much easier problems to have.
Abid Ali is a web developer in Pakistan who builds small, free tools, including OG Scope, an Open Graph and link preview checker.
Top comments (0)