Dev.to WebDev πŸ›  Dev πŸ‘ 0 πŸ“– 4 min read

Our 375 pages share two social cards, and the one on the 99 most shareable pages is 8000 pixels wide

CogniPrep's sitemap has 375 URLs. I went looking for which image each of them hands to a link preview, and found two answers, one of which is not the size it says it is. Everything is checkable from outside, so every nu

CogniPrep's sitemap has 375 URLs. I went looking for which image each of them hands to a link preview, and found two answers, one of which is not the size it says it is.

Everything is checkable from outside, so every number below comes from curl against the live site rather than from the repository.

Answer one: a 42 KB JPEG whose URL is typed out 202 times

curl -s https://cogniprep.app/games/shl | grep -o '<meta property="og:image[^>]*>'
<meta property="og:image" content="https://cogniprep.app/opengraph-image.jpg"/>
<meta property="og:image:width" content="1200"/>
<meta property="og:image:height" content="628"/>
<meta property="og:image:alt" content="SHL Practice Tests | CogniPrep"/>

The file is 1200x628 and 42,286 bytes, so the declared dimensions are honest. They are also two pixels short of the 1200x630 that every social-card guide quotes, which tells you something nice about how they got there: somebody measured the actual file instead of copying the convention. Nothing renders worse for it.

What is less nice is the other number. That absolute URL appears in 202 separate page.tsx files, once each. The root layout sets metadataBase, so a relative /opengraph-image.jpg would be resolved for free, and Next.js has a file convention (app/opengraph-image.jpg) whose entire purpose is to emit these tags for every route without a single page mentioning the image. Ours lives in public/ instead, which turns one asset into 202 string literals that a rename would have to catch.

The per-page og:image:alt is the reason it is not simply a bug: the alt text is genuinely different on each page, so these blocks were never going to be pure duplication. But the alt could vary while the URL came from one constant, and today it does not.

Answer two: the blog cards, from one builder, pointing at a hero image

99 of the 375 URLs are blog articles, and not one of those 99 page files contains the word openGraph. They all call one function:

const OG_IMAGE = `${BASE_URL}/landing/image.webp`;

/**
 * Builds the Next metadata export for an article page from its registry entry, so
 * every post gets the same canonical, OpenGraph and Twitter treatment without each
 * page restating 40 lines of it.
 */
export function buildArticleMetadata(slug: string): Metadata {
  // ...
  images: [{ url: OG_IMAGE, width: 1200, height: 750, alt: post.title }],

This is the pattern the other 202 pages should have, and it is pointed at the wrong file:

curl -s https://cogniprep.app/blogs/acer-entrance-exam-tips | grep -o '<meta property="og:image[^>]*>'
<meta property="og:image" content="https://cogniprep.app/landing/image.webp"/>
<meta property="og:image:width" content="1200"/>
<meta property="og:image:height" content="750"/>

Download that file and look at it:

curl -s https://cogniprep.app/landing/image.webp -o card.webp && file card.webp
card.webp: RIFF (little-endian) data, Web/P image, VP8 encoding, 8000x5000, Scaling: [none]x[none], YUV color, decoders should clamp

8000x5000, 95,022 bytes. The declared 1200x750 is the aspect ratio written as if it were a size. It is the right ratio, so nothing is distorted, but og:image:width and og:image:height exist so a consumer can reserve layout and decide whether to fetch at all, and ours describe an image 44 times smaller in pixel area than the one it will get.

The reason the file is that big is that it is the landing page hero. On the page itself it goes through next/image and arrives at a sane width for the device. A social card cannot do that, because og:image has to be a plain static URL rather than an optimizer URL with a width in it, so the crawler gets the untouched original. A hero image and a card image look like the same job and are not.

The format is a gamble on exactly the wrong pages

There is a second reason those 99 pages are the wrong ones to experiment on. X documents WebP as a supported card format. Facebook's own documentation lists JPEG, PNG and GIF, while plenty of secondary reports say WebP previews render anyway, and LinkedIn guidance is split enough that nobody states it confidently. I could not find a current authoritative answer for either platform, which is the finding: the format of our blog cards works or does not work depending on which crawler shows up, and blog articles are the pages people actually share.

Meanwhile the homepage manages to use both files at once:

<meta property="og:image" content="https://cogniprep.app/opengraph-image.jpg"/>
<meta name="twitter:image" content="https://cogniprep.app/landing/image.webp"/>

Facebook gets the JPEG, X gets the 8000-pixel WebP, and the two cards for the same URL are different pictures. /interview does the same thing.

See it in three commands

curl -s https://cogniprep.app/sitemap.xml | grep -c '<loc>'
curl -s https://cogniprep.app/ | grep -o '<meta \(property="og:image\|name="twitter:image\)[^>]*>'
curl -s https://cogniprep.app/blogs/acer-entrance-exam-tips | grep -o '<meta property="og:image[^>]*>'

375, then a JPEG and a WebP for the same page, then a card that claims to be 1200x750. Swap in any other URL from the sitemap: the /tests/* pages even add og:image:type of image/jpeg, which none of the others bother with.

The shape of the fix is one constant and one file convention rather than 202 edits, and the blog builder already proves the pattern works. The site those cards point at is cogniprep.app, 56 assessment providers of timed practice tests, and the 99 articles with the oversized cards are at cogniprep.app/blogs.

πŸ“° Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.