The Facebook Preview Was Broken. The Meta Tags Were Innocent.
We chased a Facebook/Messenger preview bug through Open Graph tags, image URLs, robots.txt, and range requests. The fix was hiding in Cache-Control.

We had one of those useful little humiliations this week.
A ShareScan blog post looked fine in the browser. The Open Graph tags were there. The image was the right size. The URL was public. The page had already been through the kind of checklist we usually tell other people to run.
Then the link went into a Facebook/Messenger message and the preview did not behave.
Naturally, our first instinct was to look very serious and say things like "platform cache" and "crawler variance." This is engineer for "please let the problem be somewhere else."
It was not somewhere else.
The Symptom
The page in question was a blog article about Open Graph images not showing in Slack, LinkedIn, and X. In other words, a page about broken social previews had a broken social preview.
That is not a bug. That is content strategy with commitment.
The important part: the page itself loaded correctly. The title, description, canonical URL, and image metadata all appeared in the server-rendered HTML. The image URL opened directly. Nothing obvious was missing.
So we started with the boring checks.
What We Checked First
Before changing anything, we fetched the live production URL the way a crawler might see it.
We confirmed:
- the page returned
200 - the Open Graph tags existed in the initial HTML
og:typewasarticleog:title,og:description,og:url, andog:imagewere present- the image was a public HTTPS JPEG
- the image was
1200 x 630 - the image returned
image/jpeg - range requests worked
robots.txtwas not blocking the path- the response was the same for a Facebook crawler user agent
At this point, the metadata looked annoyingly correct.
This is the dangerous moment in preview debugging. When the tags look correct, it is tempting to start nudging copy, swapping the image, adding one more property, or refreshing the same URL in a debugger until someone suggests lunch and the issue becomes tomorrow's problem.
Instead, we kept reading the response.
The Header That Looked Wrong
The HTML response had this cache header:
1cache-control: no-store, private, max-age=0, must-revalidateThat is a perfectly reasonable kind of header for private application pages. Dashboards, account screens, billing areas, and anything user-specific deserve to be treated carefully.
But this was not a billing page. It was a public blog post that we wanted crawlers, link expanders, and social platforms to fetch, understand, and cache predictably.
So our public article was effectively saying:
Hello, please build a nice share preview from this page. Also, please treat this response like it contains someone's tax return.
Not ideal.
Why Cache-Control Matters For Previews
Social preview crawlers are not normal readers. They do not sit down with a coffee, open DevTools, wait for hydration, admire the prose, and forgive a few odd headers because the intent was good.
They fetch the URL, read the response, collect metadata, fetch the image, and decide what to cache for future shares. Headers are part of that conversation.
no-store says the response should not be stored. private says shared caches should not store it. max-age=0 says it is immediately stale. None of those are the kind of signals we want on stable public content.
This does not mean Cache-Control is the cause of every Facebook preview problem. It is not magic. It will not rescue missing og:image tags, blocked assets, oversized images, bad redirects, or a stale platform cache.
But in our case, the page was public, the metadata was valid, and the cache policy was sending the wrong message.
The Fix
We added route-level headers for public blog HTML:
1cache-control: public, max-age=300, s-maxage=300, stale-while-revalidate=300That gives browsers a short cache window, gives shared caches a clear public policy, and still lets the content refresh quickly when the article changes.
We applied it to:
- individual blog posts
- the blog index
- tag archive pages
We did not apply it to private app routes or API responses that should remain uncached. This was not a heroic global header change. It was a small correction for public HTML that should behave like public HTML.
What We Should Have Done Earlier
The honest answer: looked at headers sooner.
Our debugging order was not terrible. It is still right to verify the raw HTML, the image URL, and crawler access first. But once those passed, the cache policy should have moved up the suspect list faster.
Here is the cleaner flow we would use next time:
- Fetch the final public URL with
curl -I. - Confirm it returns
200without odd redirects. - Check the raw HTML for Open Graph and Twitter Card metadata.
- Open the
og:imageURL directly. - Confirm image dimensions, content type, and file size.
- Check
robots.txtand obvious crawler blocks. - Read
Cache-Controlon both the page and the image. - Refresh the relevant platform cache only after the source looks clean.
If you skip straight to the platform debugger while the source response is still wrong, you are just asking a third party to cache your mistake with confidence.
The Small Lesson
Broken social previews are rarely about one tag.
They are usually the result of a few systems meeting briefly and not agreeing on what reality is. Your CMS has one version. Your frontend renders another. Your CDN remembers yesterday. The platform crawler shows up with very little patience. Then someone pastes the link into a chat and asks why the thumbnail is missing.
In our case, the meta tags were innocent. The image was innocent. The crawler was probably doing something reasonable with a response that did not look like public, cacheable content.
We fixed the header. Then we verified locally with the production build and checked the response again.
The slightly bruising part is that this is exactly the kind of thing ShareScan exists to make easier to spot. Which is, at least, a tidy product lesson wrapped inside a small public faceplant.
A Quick Cache-Control Checklist For Shareable Pages
For public articles, landing pages, docs, and campaign pages, check:
- does the HTML return a public cache policy?
- is the cache window short enough for edits to appear quickly?
- does the CDN or edge cache have a clear
s-maxagepolicy? - are private app routes still protected with
no-store? - does the image URL have stable, crawler-friendly headers?
- do you test the final production URL, not preview or staging?
The goal is not to cache everything forever. The goal is to make public pages look public, private pages stay private, and crawlers stop receiving mixed signals.
If your preview is still wrong after that, use the link preview debugger, then follow the Open Graph image refresh checklist. And if the image is huge, start with the Open Graph image size guide before you blame the platform.
FAQ
It can contribute to preview problems, especially when public HTML is served with private or no-store cache headers. It is not the only possible cause, but it is worth checking after metadata, image access, redirects, and robots rules.
Usually no. Public, shareable articles normally benefit from a short public cache policy. Keep no-store for private, personalized, or account-specific pages.
It depends on your publishing model, but a short public policy such as `public, max-age=300, s-maxage=300, stale-while-revalidate=300` is a practical starting point for frequently edited public content.
Often, yes. First fix the source response. Then use the relevant platform debugger or wait for the platform cache to refresh. Refreshing before the source is clean just teaches the platform the wrong thing again.
Related articles
- Link Preview Debugger: How to Find What Broke Before You ShareRead article
- Open Graph Image Not Updating? How to Refresh Stale Link PreviewsRead article
- Open Graph Image Size: The Practical 2026 GuideRead article
- Why Large og:image Files Break Social PreviewsRead article
- Slack Unfurl Debugger: Why Your Link Preview Is MissingRead article
Put the guide into practice
Check the links your team shares.
Check preview images, titles and descriptions on up to 10 pages. Review the findings, then choose what to monitor.
No account or card. Choose weekly follow-ups by email or Slack after your scan.