Twitter Card Image Size: summary vs summary_large_image
X card image sizes: 1200×600 (2:1) for summary_large_image, a square for summary. What 1200×630 and 1200×628 lose to the crop, and the tags that pick the card.
The Twitter (X) card image size depends on the card type. For summary_large_image use 1200×600 pixels, which is exactly the card's 2:1 shape. For summary, the small card, use a square image such as 600×600. Both must be under 5 MB. The card type comes from one tag, written with name, not property:
<meta name="twitter:card" content="summary_large_image"> <meta name="twitter:image" content="https://example.com/cards/my-post-1200x600.png"> <meta name="twitter:image:alt" content="Title card for the post My post">
If you already serve a 1200×630 og:image, you can skip twitter:image: X falls back to og:image, and a centred 2:1 crop removes only 15 pixels top and bottom. The rest of this page shows when that crop matters, what the summary card does to a wide image, and how to render each shape from one template. The general Open Graph sizes for every other platform are in the OG image size guide.
Tested on 2026-10-18 with Node 22.18 on macOS. The crop numbers are computed (centred crop assumed). The card template was rendered with
[email protected]Chromium at 1200×630, 1200×600 and 600×600 (no overflow at any size; the title hides in the square). The signing code below was run and reproduces the reference signature from our signed URL documentation. We did not post test cards to X for this guide, and X's developer documentation did not load for us that day, so the X limits below come from X's card documentation as last published; re-check them before you rely on the edge cases.
The two card types that use images
The Twitter card glossary entry lists all four types. For a website, the choice is between these two:
summary |
summary_large_image |
|
|---|---|---|
| Layout | small thumbnail beside title and description | full-width image above the title |
| Image shape | 1:1 | 2:1 |
| Size to render | 600×600 (any square from 144×144 up) | 1200×600 |
| Minimum (X docs) | 144×144 | 300×157 |
| Maximum (X docs) | 4096×4096, under 5 MB | 4096×4096, under 5 MB |
| Formats (X docs) | JPG, PNG, WEBP, GIF (first frame only) | same |
| Good for | docs pages, profiles, pages without a strong visual | blog posts, launches, changelogs |
SVG is not in X's list of formats. Use PNG for text-heavy cards and JPEG for photos.
What the 2:1 crop removes from common sizes
Most sites have one Open Graph image at 1200×630 and let X reuse it. Here is what a centred 2:1 crop removes from the common source sizes, and what a square crop removes when the same image lands in a summary card:
| Source | In the 2:1 large card | In the 1:1 summary thumbnail |
|---|---|---|
| 1200×600 | nothing | 300 px from each side |
| 1200×628 | 14 px from top and bottom | 286 px from each side |
| 1200×630 | 15 px from top and bottom | 285 px from each side |
| 1200×675 (16:9) | 37 to 38 px from top and bottom | 262 to 263 px from each side |
| 600×600 | 150 px from top and bottom | nothing |
Three practical rules follow:
- 1200×630 is fine for the large card as long as nothing important sits in the top or bottom 15 pixels. That rules out thin border strips and footers pinned to the edge, which are common in card templates.
- 1200×628 and 1200×630 behave the same. The
twitter_card_largepreset renders 1200×628 for teams that want the image to match the 1.91:1 shape exactly; the crop difference is one pixel per edge. - A wide image in a
summarycard loses about half its width. If you choose the small card, give it a square image designed for it, with the logo and first words in the middle.
One template, three shapes
You do not need three templates. Write the card with viewport units and flexbox instead of fixed pixel heights, and the same HTML renders correctly at 1200×630, 1200×600 and 600×600:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <style> html, body { margin: 0; width: 100vw; height: 100vh; } body { box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; gap: 2vh; padding: 6vh 6.5vw; background: #111827; color: #f9fafb; font-family: system-ui, sans-serif; } .site { font-size: max(18px, 4.4vh); color: #93c5fd; } h1 { margin: 0; font-size: max(28px, 10vh); line-height: 1.1; text-wrap: balance; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; } /* Square thumbnails are tiny: show the site name only, larger. */ @media (aspect-ratio: 1 / 1) { h1 { display: none; } .site { font-size: 14vh; text-align: center; } } </style> </head> <body> <div class="site">example.com</div> <h1>Your post title, escaped on the server</h1> </body> </html>
The aspect-ratio media query is the useful trick here. A title that is readable in a 1200-pixel-wide card is unreadable in a thumbnail the size of a fingertip, so the square variant shows only the name or logo. Escape the title on the server before it goes into the HTML; the OG image generation guide explains why and has a tested escaping helper.
Render each shape with a preset
RenderScreenshot presets set the viewport for the two wide shapes. The square has no preset, so pass width and height directly. Each URL below is a signed URL, so it can sit in a public meta tag without exposing an API key.
| Shape | Request parameters | Output |
|---|---|---|
| Open Graph, 1.91:1 | preset=og_card |
1200×630 PNG |
| X large card, exact 2:1 | preset=twitter_card |
1200×600 PNG |
| X large card, 1.91:1 | preset=twitter_card_large |
1200×628 PNG |
| X summary card, square | width=600&height=600 |
600×600 PNG |
Note that the twitter_card preset produces the 2:1 shape that summary_large_image uses, not the square thumbnail of the summary card.
The signing function is the Node.js listing from the signed URL documentation:
// sign.mjs import { createHmac } from 'node:crypto'; // Same encoding as the API's signature check: space -> "+", only A-Z a-z 0-9 - _ . ~ kept. const encode = (v) => encodeURIComponent(String(v)) .replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase()) .replace(/%20/g, '+'); export function signedScreenshotUrl(params, { keyId, secret, expiresAt }) { const fields = { ...params, key_id: keyId, expires: Math.floor(expiresAt.getTime() / 1000) }; const query = Object.entries(fields).map(([k, v]) => `${encode(k)}=${encode(v)}`).sort().join('&'); const signature = createHmac('sha256', secret).update(query).digest('hex'); return `https://api.renderscreenshot.com/v1/screenshot?${query}&signature=${signature}`; }
And the three image URLs for one post:
// cards.mjs: signed image URLs for one post in the three shapes X and Open Graph use. import { signedScreenshotUrl } from './sign.mjs'; const keys = { keyId: process.env.RS_PUBLIC_KEY_ID, // rs_pub_... secret: process.env.RS_SECRET_KEY, // rs_secret_..., server only expiresAt: new Date(Date.now() + 21 * 24 * 60 * 60 * 1000), // inside the 30-day maximum }; const card = (shape) => `https://example.com/cards/my-post?shape=${shape}&v=1760745600`; const cache = { cache_available: 'true', cache_ttl: 2592000 }; export const images = { og: signedScreenshotUrl({ url: card('wide'), preset: 'og_card', ...cache }, keys), // 1200x630 large: signedScreenshotUrl({ url: card('wide'), preset: 'twitter_card', ...cache }, keys), // 1200x600 square: signedScreenshotUrl({ url: card('square'), width: 600, height: 600, ...cache }, keys), }; for (const [shape, url] of Object.entries(images)) console.log(shape, url.replace(/signature=\w+/, 'signature=...'));
cache_available=true lets repeat crawler fetches inside the cache TTL reuse the first render for 0 credits; without it, every fetch is a new render. Each shape is a separate render, because the preset or viewport is part of the cache key, so serving both og:image and a dedicated twitter:image costs two renders per post instead of one. The expiry must be at most 30 days ahead; the caching reference and the signed URL page explain both rules.
Which tags to write
One image for everything (the default we recommend):
<meta property="og:image" content="{og url}"> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="630"> <meta property="og:image:alt" content="{description}"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:image:alt" content="{description}">
A separate exact 2:1 image for X, when your design uses the full height:
<meta property="og:image" content="{og url}"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:image" content="{large url}"> <meta name="twitter:image:alt" content="{description}">
The small card with a square image:
<meta name="twitter:card" content="summary"> <meta name="twitter:image" content="{square url}"> <meta name="twitter:image:alt" content="{description}">
In HTML attributes, a signed URL's & characters must be written as &. Template engines that escape attribute values do this for you; string concatenation does not.
How X picks the image and title
X reads its own tags first and falls back to Open Graph for the rest. That fallback is why most sites need only one twitter: tag:
- Card type comes only from
twitter:card. There is no Open Graph equivalent, so without this tag you do not getsummary_large_image. - Image comes from
twitter:image, and if that is missing, fromog:image. - Title and description come from
twitter:titleandtwitter:description, else fromog:titleandog:description. - Alt text comes from
twitter:image:alt. Set it explicitly; screen reader users on X hear it.
Because the image falls back, a page with a correct og:image and a twitter:card tag but no twitter:image is complete. Add twitter:image only for one of the two reasons above: a design that needs the exact 2:1 shape, or the square summary card.
To see the result before you share, compose a post with the link on X without sending it; the composer shows the card it will use. X no longer offers a separate card validator with a preview.
Troubleshooting X cards
| Symptom | Cause | Fix |
|---|---|---|
| Small card instead of large | twitter:card missing or set with property= |
<meta name="twitter:card" content="summary_large_image"> |
| Title or footer cut off | Content in the outer 15 px of a 1200×630 image | Move it inward, or serve a 1200×600 twitter:image |
| Logo missing from the small card | Wide image center-cropped to a square | Serve a square image for summary |
| No image at all | Image over 5 MB, SVG, or blocked for the crawler | PNG or JPEG under 5 MB; check robots.txt for Twitterbot |
| Old image after a change | The card is cached by URL | Change the image URL (version parameter), then post the link again |
If the image does not appear on any platform, the cause is usually not the size. Work through the twelve causes of a missing OG image, which comes with a script that checks each one. To render the three shapes from your own template, create a free account; it includes 150 credits a month.
Last tested 2026-10-18.
Related in this topic
- Generate OG Images From HTML: A Production Guide Generate Open Graph images from an HTML template: platform size limits, escaping, signed URLs without redirects, cache-safe expiry and a Next.js example.
- The State of OG Images: A 10,000-Site Crawl We crawled the top 10,000 home pages for og:image and Twitter card tags: adoption, sizes, formats, file weight and broken image URLs, with our method.
- Next.js OG Images: opengraph-image.tsx vs a Route Handler Dynamic OG images in Next.js 16: opengraph-image.tsx, the tags it emits, measured static vs per-request timings, fonts, and when a screenshot route wins.
- OG Image Not Showing? 12 Causes and a Checker Script OG image not showing in Slack, LinkedIn or X? Twelve causes in the order to check them, a tested Node script that finds each one, and the fix for every case.
- OG Image Size: 1200×630, Crops and File Limits The right OG image size is 1200×630 (1.91:1). Platform limits with sources, how many pixels each preview shape crops, and measured file sizes at 1x and 2x.
- Rails OG Images: meta-tags, an ERB Card and Signed URLs Dynamic Open Graph images in Rails 8: an ERB card route, the meta-tags gem, a signed og:image helper, Minitest tests and what allow_browser does to crawlers.