Fix link previews blocked by Cloudflare and other bot protection
A link preview blocked by Cloudflare, DataDome or Akamai shows a challenge page where the title, description and image should be. Chat apps, bookmark managers, CRMs and newsletter editors hit this on the links their users share most. On Pro plans the Metadata API routes the request through its built-in proxy, so the link unfurls into the real card.
A Cloudflare challenge page has no metadata worth showing
Antibot services answer automated requests with a verification page. Its title is “Just a moment…”, its description is empty and it has no image, so a naive unfurler renders that for every link to the site. News outlets, marketplaces and social networks sit behind this kind of protection, and those are the links people paste most.
The usual fixes do not hold. Changing the user agent does not help when the target is judging the origin IP, and datacenter ranges are the first ones it rejects. Renting a proxy list means rotating IPs, watching ban rates and paying a second vendor, all to keep a feature working that should be one HTTP call.
On Pro plans, Microlink includes automatic proxy resolution: it identifies the antibot provider and routes the request through a dedicated resolution path over a rotating proxy pool. When it detects the block and no proxy was used, the API fails with EPROXYNEEDED instead of handing you the challenge page as metadata.
How to unfurl protected links through the built-in proxy
Try the direct request first, retry through the proxy on EPROXYNEEDED, and cache the preview so the proxy path runs once per URL. The proxy guide covers the same pattern for every workflow.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const { title, description, image } = await microlink.metadata(
'https://hard-target.com/article',
{ proxy: true }
)proxy: true sends the request through the managed pool with nothing else to configure. The response is the usual normalized object: title, description, image, logo, publisher and the rest.
import createClient from 'microlink.io'
const microlink = createClient({
apiKey: process.env.MICROLINK_API_KEY
})
const preview = async url => {
try {
return await microlink.metadata(url)
} catch (error) {
if (error.code !== 'EPROXYNEEDED') throw error
return microlink.metadata(url, { proxy: true, retry: 3, ttl: '1d' })
}
}Direct requests skip the extra hop, so the proxy only runs for targets that reject them. retry adds server-side attempts for intermittent challenges and ttl caches the unblocked preview for a day.
curl 'https://pro.microlink.io/?url=https%3A%2F%2Fhard-target.com%2Farticle&proxy=true&meta.title=true&meta.description=true&meta.image=true' \
-H 'x-api-key: $MICROLINK_API_KEY'proxy is a Pro option, so the URL targets pro.microlink.io and carries your API key as a header. The meta object keeps detection to the three fields a card renders.
- proxy true for automatic resolution, an object with location to pin a country, or url for your own proxy server. Pro plans.
- retry Server-side retries with exponential backoff on unexpected browser errors. Default 2.
- ttl How long the unblocked preview stays cached, from 1 minute to 31 days. Default 24 hours. Pro plans.
- meta Restrict detection to title, description and image for a lighter request.
- ping On by default: image and logo URLs are verified as reachable before they are returned.
Confirm the route with the x-fetch-mode response header: any value prefixed with proxy-, such as fetch-proxy or prerender-proxy, means the request went through the proxy.
Why link previews need the proxy inside the metadata API
Unfurling a link is one step in your product. It should stay one step even when the target pushes back.
The same option unblocks screenshots of blocked websites and Markdown from bot-protected pages.
The error surfaces on every plan. Routing through the proxy needs a Pro key; see pricing for the plans.
When not to: the proxy gets past bot blocks, not logins or paywalls. Content behind authentication needs forwarded headers, and only where you are permitted to fetch it.
FAQ
Why does my link preview show “Just a moment…” as the title?
How do I fix a link preview blocked by Cloudflare?
Is the metadata API proxy included in Pro plans?
Can I pin the metadata proxy to a specific country?
Should every link preview request go through the proxy?
Solve the next problem with the same API
Link previews at scale
Region-specific metadata
Fix missing or wrong og:image
Metadata from single-page apps
Screenshot blocked websites
Markdown from bot-protected pages
Ready to unfurl every link?
One option, no proxy list, real previews for the sites that block everyone else. Get a Pro key and fix your first blocked preview today.