Tag priority order
CardyB resolves each card field using first-hit-wins fallback:
| Card field | Priority order |
|---|---|
| Title | og:title → twitter:title → <title> |
| Description | og:description → twitter:description → <meta name="description"> |
| Image | og:image → twitter:image |
If nothing matches for a field, it’s left empty. A card with no title still renders using the URL as fallback text.
No twitter:card type support
Bluesky ignores twitter:card entirely. There’s no summary vs. summary_large_image distinction: all link cards render in the same format. You don’t need it, and setting it won’t change anything.
AT Protocol lexicon mapping
Card data gets written into the post record as an app.bsky.embed.external entry:
{
"$type": "app.bsky.embed.external",
"external": {
"uri": "https://example.com/page",
"title": "Page Title",
"description": "Page description text",
"thumb": {
"$type": "blob",
"ref": { "$link": "bafkrei..." },
"mimeType": "image/jpeg",
"size": 54321
}
}
}
The uri, title, and description are stored as plain text. The thumb is a blob reference, and the original og:image URL is not kept in the record. Once it’s baked into the post, it lives on the AT Protocol network independently of your server.
Recommended minimal setup
Standard Open Graph tags are all you need:
<meta property="og:title" content="Your Page Title">
<meta property="og:description" content="A brief description of your page.">
<meta property="og:image" content="https://example.com/image.jpg">
No Bluesky-specific meta tags exist. If you already have OG tags for other platforms, Bluesky uses them as-is.
og:image:width and og:image:height
Bluesky ignores og:image:width and og:image:height. The image is downloaded and processed regardless. These tags won’t cause issues; they’re just skipped.