On this page
- scope
- why your X card might not be showing — the four buckets
- bucket 1: meta tags — the four that have to exist
- bucket 1.5: the og: fallback chain (and the trap)
- bucket 2: image requirements — the spec that trips everyone
- bucket 3: crawler access — robots.txt, SSL, server hygiene
- bucket 4: when your card is fine and nobody saw the post anyway
- the Card Validator situation: what happened, what to use instead
- the post-rebrand checklist
- debugging flowchart: 60-second triage
- a parallel note on Facebook / LinkedIn
- frequently asked
- related
scope
You shipped a link on X (formerly Twitter). The card didn't render. You searched. Every guide told you to open the Card Validator. The Card Validator hasn't shown a preview since mid-2022.
This is the current reference: the four meta tags that still work, the image and HTTPS requirements that trip everyone, the three tools that replaced the Card Validator, and the part nobody else writes about — X actively suppresses posts with external links, so your card can render perfectly and the post can still appear to "not show" because nobody saw it. Targets both X and Twitter terminology. Dev-facing.
why your X card might not be showing — the four buckets
Most guides cover three. We cover four. Ordered by frequency.
| # | Bucket | ~Share | What it is |
|---|---|---|---|
| 1 | Meta tags wrong, missing, or in the wrong place | ~60% | The HTML problem. |
| 2 | Image accessibility, size, format | ~20% | The 401/wrong-content-type/relative-URL problem. |
| 3 | Crawler access — robots.txt, SSL, CDN bot rules, server timeout | ~10% | Twitterbot got blocked, blocked itself, or timed out. |
| 4 | Account- or platform-level suppression | ~10% | Tags are fine. The algorithm is the problem. |
Buckets 1–3 are the card-rendering pipeline (meta tags → Twitterbot → render). Bucket 4 is the distribution layer (algorithm → impressions). They look identical from outside and require different fixes. If buckets 1–3 are verified and the post is still invisible, skip to "bucket 4".
bucket 1: meta tags — the four that have to exist
Four required, two optional-but-recommended.
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Your headline, ≤70 chars">
<meta name="twitter:description" content="Your description, ≤200 chars">
<meta name="twitter:image" content="https://example.com/og-image.png">
<meta name="twitter:site" content="@yourhandle">
<meta name="twitter:creator" content="@authorhandle">
Rules:
- All tags go inside
<head>, not<body>. Twitterbot stops parsing past</head>for card metadata. - Server-rendered, not JS-injected. Twitterbot doesn't execute JavaScript meaningfully. If meta tags are written by client-side React/Vue/Svelte after hydration, Twitterbot sees nothing. SSR or prerender — see bucket 3.
- Character limits: title ≤70 chars, description ≤200.
- Post-rebrand naming gotcha: attributes are still
twitter:*, notx:*. X did not rename them. People assume the rebrand renamed the spec, replacetwitter:cardwithx:card, and silently break every preview on the site.
the four card types
| Type | Use case | Image |
|---|---|---|
summary |
Compact card, small square thumbnail | Optional (144×144 min) |
summary_large_image |
Standard content card, hero image | Required (300×157 min, 1200×630 recommended) |
app |
iOS/Android app install | App store IDs required |
player |
Embedded video/audio | HTTPS iframe endpoint required |
For any modern content page, use summary_large_image. The other three are special-purpose and still functional post-rebrand.
bucket 1.5: the og: fallback chain (and the trap)
Single most-missed nuance in the entire top-5 SERP. X reads tags in this priority order:
twitter:title→ falls back toog:title→ falls back to<title>twitter:description→ falls back toog:description→ falls back to nothingtwitter:image→ falls back toog:image→ falls back to no image (small text-only card)twitter:carddoes not fall back. Missing or invalid value = no card at all.
the empty-string trap
<meta name="twitter:image" content="">
An empty twitter:image="" does not trigger fallback to og:image. X reads it as "explicitly empty" and stops. Same for twitter:title="" and twitter:description="". The fallback only fires when the tag is entirely absent. CMSs that auto-emit twitter:* tags for every page — including ones where the field is blank — break the OG fallback this way. Fix: populate the field, or have the CMS suppress emission when the value is empty.
practical implementation pattern
If you already maintain og:* tags for Facebook, LinkedIn, Slack, Discord, iMessage, you only strictly need twitter:card as your X-specific tag. The rest cascades. Minimum viable head:
<meta name="twitter:card" content="summary_large_image">
<meta property="og:title" content="Your headline">
<meta property="og:description" content="Your description">
<meta property="og:image" content="https://example.com/og-image.png">
<meta property="og:url" content="https://example.com/page">
<meta property="og:type" content="article">
Cleanest pattern for sites that don't want platform-specific duplication.
bucket 2: image requirements — the spec that trips everyone
| Card type | Min dimensions | Recommended | Aspect ratio | Max file size |
| --------------------- | -------------- | ----------- | ------------- | ------------- |
| summary | 144×144 | 300×300 | 1:1 | 5 MB |
| summary_large_image | 300×157 | 1200×630 | ~1.91:1 (2:1) | 5 MB |
Accepted formats: JPG, PNG, WEBP, GIF. (Animated GIFs render as a still frame.)
Eight things that break image rendering:
- Image isn't publicly accessible. Behind login, on staging, IP-restricted. Twitterbot got a 401/403/404. Open the URL in an incognito window; if it doesn't render, X can't read it.
- Served over HTTP, not HTTPS. X requires HTTPS for the page and the image. Mixed-content fails silently.
- Too small. Below 300×157 → no image on
summary_large_image. Below 144×144 → fails entirely. - Too large. Over 5 MB → Twitterbot stops downloading.
- Wrong
Content-Typeheader. Server returnstext/html(image URL routed to a 404 page) orapplication/octet-streaminstead ofimage/png|jpeg|webp|gif. Twitterbot rejects. - Relative URL.
twitter:imagecontent must be absolute (https://…), not/og/image.png. - CDN bot rules block Twitterbot. Cloudflare bot fight mode is the most common culprit. Fastly origin shielding and AWS Shield rules also hide images from external crawlers. Whitelist
Twitterbot/1.0. - Hotlink protection or restrictive origin policy. S3 buckets with referer restrictions, image CDNs with hotlink protection, third-party hosts that block non-browser user agents.
Quick verification:
curl -I -A "Twitterbot/1.0" "https://example.com/og-image.png"
# expect: HTTP/2 200, Content-Type: image/png (or jpeg/webp/gif)
If you get anything other than 200 and an image/* Content-Type, that's your bug.
bucket 3: crawler access — robots.txt, SSL, server hygiene
robots.txt
If you Disallow: / for Twitterbot — or wildcard-disallow all bots — no cards render. Check curl https://example.com/robots.txt. Explicit allow pattern:
User-agent: Twitterbot
Allow: /
Worth allowing alongside (the other social-preview crawlers): facebookexternalhit, LinkedInBot, Slackbot, Discordbot, WhatsApp, TelegramBot, Pinterestbot.
HTTPS / TLS
X requires TLS 1.2 or later. Self-signed certs, expired certs, broken cert chains all silently break the crawl. Test at SSL Labs. Anything below an A rating risks failures. SNI must be configured correctly if the origin serves multiple hostnames.
server response time
Twitterbot's timeout is approximately 10 seconds. Slow first-byte responses cause silent crawl failures with no publisher-side error. CDN-cache your OG endpoints. If you generate OG images on the fly via a serverless function, verify cold-start latency is under 10 seconds — Vercel/Netlify cold starts on under-provisioned plans regularly exceed this.
SPA / client-side rendering
If meta tags are written by JavaScript after page load, Twitterbot doesn't see them. Options: server-side render (Next.js, Nuxt, Astro, Remix, SvelteKit, Hugo, Eleventy); use a prerender service (Prerender.io, Rendertron) that detects Twitterbot/1.0 and serves rendered HTML; or serve a static OG-meta-only variant via redirect/proxy specifically to Twitterbot. Same crawler-vs-SPA problem as in user-agent detection contexts — identical underlying mechanic.
bucket 4: when your card is fine and nobody saw the post anyway
If you've reached this section: meta tags checked, image works, robots.txt allows Twitterbot, and the card renders in the Tweet Composer. But when you publish, the card seems to disappear, or the post gets 30 impressions and stops.
Not a bug in your tags. The algorithm.
the link-suppression policy, named directly
X shadowbans posts containing external links. Using that word because it's what's happening. The euphemisms — "deprioritized," "deemphasized," "reduced reach" — are softer versions of the same mechanic: the algorithm assigns link posts to a reduced-distribution tier, with no user notification.
Sourcing:
- Elon Musk publicly stated in October 2023 that the algorithm "tries to optimize time spent on X, so links don't get as much attention." Axios reported this directly.
- Cybernews documented the link-suppression behavior with cited reach reductions of 50–90%.
- Independent engagement audits (e.g. Jesse Colombo, March 2024) cite reductions up to 94%.
- As of early-2025 reporting, non-Premium accounts posting links see near-zero median engagement compared to text-only posts from the same account.
Platform policy, not a bug. The card-rendering pipeline runs correctly. The distribution layer downranks. Both true simultaneously.
account-level diagnosis (the part nobody writes about)
Between "tags are right" and "the algorithm hates you" is an intermediate layer: account standing. None documented; patterns are observable across enough creator accounts to be reliable.
- Brand-new accounts (under ~30 days, under a few hundred posts) see suppressed card rendering until X's spam-detection warms to them. Cards may not render at all even with perfect tags. Workaround: post non-link content for a few weeks first.
- Link-history flags. If your account previously posted links flagged as spam/low-quality (mass-reported, on an X-internal blocklist, redirecting through a domain X considers spammy), future link posts are silently downranked. No notification. Signal: lower impressions on link posts vs text-only from the same account.
- Monetization-eligibility downranking. Creator Program participants report outbound links reduce monetization weighting — the algorithm reads them as siphoning ad-eligible attention off-platform. Observed, not documented.
- Domain-level reputation. If your shortener or destination has been used at scale by spam accounts, X downranks regardless of who posted.
bit.ly/tinyurlcarry heavy historical baggage and have been measurably penalized; clean branded short domains avoid this. Linkboo's approach to link infrastructure is built around this — a clean, branded short domain isn't a variable when you're debugging card visibility.
Premium-account caveat
Premium accounts see less throttling. Reported median engagement on link posts is roughly 0.25–0.30% for Premium vs near-zero for free accounts. Stated as observed reality, not a recommendation either way.
the "new link experience" caveat
X announced a "new link experience" in late 2025 to soften the link penalty on iOS. As of May 2026, implementation is partial/regional. Don't assume it applies to your audience without verifying current observed behavior on your own account.
working creator workarounds
Observed patterns, listed without endorsement.
- Link as a reply to your own tweet. Text-only main tweet; link in the first reply. Main-tweet reach is normal; link is one tap away.
- Screenshot of what's at the link; link in a reply. Image carries engagement; link is one tap for the curious.
- Post from a Premium account. Direct cost, partial benefit.
- Use link-in-bio. The X profile bio link is not suppressed. If you have one primary destination, a clean short link in bio consistently outperforms in-post links. (Part of the broader linkboo thesis on link tooling — predictable infrastructure means fewer unknowns when something breaks.)
- "RT for the link in DMs." Old-school, still works.
- Pin a tweet with the link. Pinned tweets escape some timeline downranking; impressions accumulate over time.
The technical pipeline (meta tags → Twitterbot → render) works as documented. The distribution layer (algorithm → impressions) is the part that changed under X's current ownership. Knowing which layer your problem lives in is the entire debugging skill.
the Card Validator situation: what happened, what to use instead
Every other guide in the top 5 still tells you to use the Card Validator. The Card Validator has been hollowed out since 2022.
what happened
- Twitter's official Card Validator (
cards-dev.twitter.com/validator) was the canonical debug tool for ~8 years. - Mid-2022: the preview functionality was removed. URL still loads. Render preview is gone.
- X has not shipped a replacement. The X Developer Community has multiple unresolved threads asking when one will arrive; the effective answer has been "use the Tweet Composer."
- The validator URL is still useful for one narrow purpose: forcing Twitterbot to re-crawl your URL and invalidate the cache. Cache bust still functions; preview render does not.
the three things that replaced it
1. The Tweet Composer (most accurate)
Start composing a tweet, don't publish, paste your URL, wait ~5 seconds. The card preview renders inline if your meta tags are valid. Renders against the live X frontend — closest thing to ground truth available in 2026. Requires an X account. Use this first; if the card renders here, your tags are fine.
2. curl with the Twitterbot user agent (technical gold standard)
curl -A "Twitterbot/1.0" -L "https://example.com/your-page" | grep -i "twitter:\|og:"
The exact bytes Twitterbot would receive. If your tags don't appear in the output, they're either missing from server-rendered HTML or being filtered/blocked by your CDN. No fancier tool tells you more.
Variants:
# headers + redirect chain
curl -A "Twitterbot/1.0" -I -L "https://example.com/your-page"
# TLS handshake (some configs reject older bot clients)
curl -A "Twitterbot/1.0" -v "https://example.com/your-page" 2>&1 | grep -i "ssl\|tls"
# image fetch
curl -A "Twitterbot/1.0" -I "https://example.com/og-image.png"
To verify what user-agent string actually reaches your endpoint when a crawler hits it, the user-agent checker tool gives you a logged endpoint to point at.
3. Third-party validators (lowest friction)
Not guaranteed to match X's live render, but they catch tag-level errors quickly and don't require an X account:
- opengraph.xyz — free, no signup, generic OG + Twitter card preview
socialrails.com/free-tools/x-tools/card-validator— explicit X focus, 2026-currentopentweet.io/tools/x-card-validator— same nichepostel.app/twitter-x-card-validator— samemeta-tag-checker.com— broader OG/Twitter checker
Order of accuracy: Composer > curl > third-party.
the post-rebrand checklist
Haven't changed: meta tag attribute names (still twitter:*, not x:*); card types (summary, summary_large_image, app, player); the og:* fallback chain; Twitterbot user agent (Twitterbot/1.0); image specs.
Have changed: the Card Validator preview (gone, mid-2022); the algorithmic treatment of links (suppression, post-acquisition).
If you wrote a Twitter card implementation in 2020, the HTML still works. The tools and the algorithm shifted. Don't refactor your meta tags assuming the rebrand broke them.
debugging flowchart: 60-second triage
- Paste URL into Tweet Composer. Card renders? → go to step 5. Doesn't render? → step 2.
curl -A "Twitterbot/1.0" -L https://example.com/yourpage | grep -i "twitter:\|og:"—twitter:*tags appear in output? No → fix server-rendering (bucket 1). Yes → step 3.- Open the
twitter:imageURL in an incognito browser. Does it render? No → fix image access (bucket 2). Yes → step 4. - Check
robots.txtforDisallowonTwitterbot. Check SSL grade. Check CDN bot rules. (Bucket 3.) - Card renders in Composer but the post got buried? → bucket 4. Algorithm, not tags. Nothing about the HTML will fix it.
Buckets 1–3 are problems you can fix. Bucket 4 is a problem you work around.
a parallel note on Facebook / LinkedIn
Same og:* tags drive previews on Facebook, LinkedIn, Slack, Discord, and iMessage. If you're debugging X card failures, you're probably seeing the same issue on adjacent platforms. Facebook's preview pipeline is still actively maintained — Meta's Sharing Debugger works the way Twitter's used to. The Facebook equivalent of this problem and the still-functional debugger walkthrough live in /guides/facebook-link-preview-not-updating and /guides/facebook-link-preview-debugger-guide.
If previews fail across multiple platforms simultaneously, the root cause is almost always bucket 2 (image accessibility) or bucket 3 (crawler access) — both platform-agnostic.
frequently asked
Why won't my Twitter card show even though my tags look right?
Three likely causes: (1) the page isn't server-rendered and Twitterbot sees an empty HTML shell; (2) the twitter:image URL is relative, behind auth, or served with the wrong Content-Type; (3) twitter:image="" is an empty string instead of entirely absent, which breaks the og:image fallback. Run curl -A "Twitterbot/1.0" -L https://yoursite.com/yourpage and inspect what comes back.
What replaced the X Card Validator?
Officially, nothing. X removed the preview from cards-dev.twitter.com/validator in mid-2022 and never shipped a replacement. The three working alternatives in 2026: Tweet Composer (paste your URL into a draft to see the live render), curl -A "Twitterbot/1.0" for raw byte verification, third-party validators like opengraph.xyz/socialrails/opentweet/postel. Composer is most accurate; curl is most technical; third-party validators have lowest friction.
Does X still read twitter:image tags after the rebrand?
Yes. Attributes are still twitter:*. X did not rename them to x:*. Card types (summary, summary_large_image, app, player) are also unchanged. If you replaced twitter: with x: assuming the rebrand renamed the spec, you silently broke every preview on your site — revert it.
Should I use summary or summary_large_image?
summary_large_image for any modern content page with a hero image (blog, product, landing). summary for compact UI cards where a square thumbnail is enough. Default to summary_large_image — it occupies more timeline real estate and gets meaningfully higher engagement.
Why does my Twitter card show in some places but not others?
Two causes: (1) X caches cards per URL and re-indexes roughly every 7 days, so older shares may show stale previews while newer ones show the corrected version; (2) the card may render in Composer (live render) but not in the published post if your account is algorithmically suppressed and the post is being rendered text-only to other users.
How long does X cache Twitter cards?
Approximately 7 days, keyed per URL. To bust the cache, submit the URL through the Card Validator's still-functional cache-refresh endpoint, or append a query string (?v=2) to force X to treat it as a new URL.
How do I force X to re-crawl my page?
In order of effectiveness: (1) paste into cards-dev.twitter.com/validator and submit — preview is gone, cache invalidation still fires; (2) append a query string (?v=2, ?cache=bust); (3) wait ~7 days for the natural cache cycle. Twitterbot still needs to crawl successfully when it re-fetches — if the bug was in buckets 1–3, cache bust alone won't fix anything.
Does X suppress posts with links?
Yes. Musk has publicly stated the algorithm "tries to optimize time spent on X, so links don't get as much attention." Axios and Cybernews cite reach reductions of 50–90%; independent analyses cite up to 94%. Platform policy, not a bug. Your card can render perfectly and the post can still receive near-zero impressions because the algorithm placed it in a reduced-distribution tier. Workaround: text-only main tweet, link in a reply.
Can a non-Premium account get a link preview to show?
Yes — the card itself renders fine. What changes is distribution: non-Premium accounts posting links see near-zero median engagement compared to text-only posts from the same account. The card is rendering; the post is being shown to almost no one. Premium sees partial relief (~0.25–0.30% median engagement on link posts), not full elimination.
Do og:* tags work as a fallback on X?
Yes. X reads twitter:title → og:title → <title>, twitter:description → og:description, twitter:image → og:image. If you maintain og:* for Facebook/LinkedIn/Slack/Discord/iMessage, you only strictly need twitter:card as your X-specific tag. Caveat: twitter:card does not fall back — if missing or invalid, no card renders.
Why doesn't an empty twitter:image="" tag fall back to og:image?
X reads twitter:image="" as explicitly empty and stops — it does not look at og:image. The fallback only fires when twitter:image is entirely absent. Same for twitter:title="" and twitter:description="". CMSs that auto-emit empty twitter:* tags break the OG fallback this way. Fix: populate the field or suppress emission of empty tags.
Do app and player cards still work on X?
Yes. Both remained functional through the rebrand. app cards require iOS/Android app store IDs; player cards require an HTTPS iframe endpoint and a fallback static image. Documentation is intermittently updated; rendering still works in production. Validate new player cards via Tweet Composer — third-party validators don't always render them correctly.
related
- Up to Cluster C hub: Firebase Dynamic Links replacement — the technical authority hub for link infrastructure
- Facebook equivalent of this problem:
/guides/facebook-link-preview-not-updating - Facebook's still-functional debugger, by contrast:
/guides/facebook-link-preview-debugger-guide - Engineering view of webview behavior: in-app browser cookies, explained
- User-agent detection in adjacent contexts: /guides/detect-tiktok-in-app-browser-useragent
- When your link gets flagged on another platform: /guides/this-link-may-be-unsafe-instagram
- The Instagram equivalent — domain-level blocking: /guides/instagram-link-blocked-domains
- If X is your primary distribution channel: /for/x
- Where Twitterbot-style crawler issues meet OAuth flow breakage: /fix/oauth-redirect-broken-in-app-browser
- Verify what user-agent is hitting your endpoint: /tools/useragent-checker
- Which UA is loading your page: /tools/in-app-browser-detector
- QR alternative for distribution where link cards aren't reliable: /tools/qr-code-generator
- Linkboo's API for programmatic link management and the docs hub
If you're wiring card-debugging into a content workflow, the docs hub covers the API surface for programmatic preview validation.