SlowDen

Your og:image is probably the wrong shape

By Liza · SlowDen · Published 12 August 2026

Every link we had ever shared rendered as a cropped slice of our own logo. The image was 1695 × 387 — a 4.38:1 strip — and the card type our pages declare renders at about 1.91:1. Nothing warned us, because nothing in that pipeline treats a wrongly shaped image as an error.

Before building a replacement we measured nine other browser-game portals to see what a correct one looks like in the wild. Six of the nine served a measurable image, and exactly one of those matched both the card type it declared and its own declared dimensions. Ours was the worst number in the table.

The short version. A social card has a fixed shape. Your image gets fitted into it and the remainder is discarded — silently, with no error and no console message. So the defect is invisible from inside your own site: the page validates, the tag is present, the image loads. You only ever see it on somebody else’s platform, in a preview you are not looking at.

What shape is an og:image supposed to be?

It depends on one other tag, which is the part people miss. twitter:card chooses which of two layouts a platform uses:

Declaring one and supplying an image shaped for the other is the whole bug. And because the platform fits rather than rejects, both mistakes look completely fine on your end. The tag is there. The image resolves. Your validator is happy. Somewhere else, a person sees the middle third of your wordmark.

Worth saying plainly, since these tags have a reputation for being fiddly: the ratio is what matters, not the exact pixel count. A 2400 × 1260 image is the same shape as a 1200 × 630 one and previews identically.

What was our own site actually serving?

We read the number out of the PNG’s own IHDR header rather than trusting the filename or the CSS, and by two independent routes — once off the file on disk, once by fetching the first 16 KB of the live URL. Both gave the same answer.

MeasurementValue
The file we were serving as og:imagethe site wordmark, 36,527 bytes
Its real pixel size1695 × 387
Its aspect ratio4.380 : 1
What our declared card renders at~1.905 : 1
Mismatch2.30×
Pages affected23 of 235
og:image:width / og:image:height declaredneither

At 2.3× too wide, more than half the width of the image is cut. What survives is a centre slice of a wordmark with the first and last letters gone — which is worse than no image at all in one specific way: it looks like a rendering bug on the platform, not a choice.

What do other browser-game portals serve?

This is the part we had not seen written down anywhere, so we measured it. One unauthenticated GET per homepage, the Open Graph tags read out of the served HTML, then a Range: bytes=0-16383 request against each image so we could read its real dimensions out of the file’s own header without downloading the whole thing. Measured 12 August 2026.

SiteCard declaredog:image, real sizeRatioDimensions declared in the markup
Pokisummary_large_image1200 × 6301.9051200 × 630 — matches
Playgamasummary2400 × 12601.905none
CrazyGamessummary_large_image1200 × 6002.0001200 × 630 — does not match
Armor Gamessummary_large_image1200 × 8251.455none
Y8summary_large_image500 × 5001.000500 × 500 — matches the file
itch.iosummary_large_imageno og:image
Kongregatenoneno og:image
GameJoltnoneno og:image
Newgroundsnot measured — returned 403 to our request
SlowDen, before thissummary_large_image1695 × 3874.380none

Reading it honestly, four things stand out:

What this table is not. It is not a ranking, and it is not a criticism of anyone. Every site in it is a larger platform than ours, and the worst number in the table is ours. It is also a single unauthenticated request per homepage on one day: a site can serve different markup to a logged-in visitor or to a specific crawler, inner pages often differ from the homepage, and Newgrounds returned 403 to us so it is recorded as unmeasured rather than guessed at. The point of the table is only this — if a defect this visible is this common among sites this large, checking yours is worth five minutes.

Why did measuring first cut the job by ninety per cent?

The instinct on finding a broken share image is to build a card and put it on every page. We have 235 pages, so that is 235 edits.

Counting first said otherwise. 212 of the 235 were never part of the defect. Those pages point og:image at the individual game’s own thumbnail rather than at any site-level image. We sampled 25 of the 200 catalogue thumbnails at random by range request, and 25 of 25 came back 1920 × 1080; a full sweep of all 200 resolved 196 at that size and left 4 unresolved.

16:9 is 1.778:1. Into a 1.91:1 card that is a trivial crop — a few per cent off the top and bottom. Those pages were fine.

They were also better than fine. A preview showing the actual game tells a reader what they are about to click; a brand card tells them the domain, which the platform already prints underneath. Putting a generic card on 212 game pages would have been a downgrade dressed up as a fix — the kind you never detect afterwards, because everything you changed still validates.

So 23 pages needed the new card, not 235. The general rule we took from it:

Count how many things actually have the defect before you build the thing that repairs it. The audit is not preparation for the fix. It is the first draft of the fix, and it is the step that stops a repair doing damage of its own.

Why we did not declare width and height on every page

og:image:width and og:image:height let a platform lay out the preview before it has fetched the image. That is a genuine benefit and we do declare them — on the 23 pages using our own card.

We deliberately did not add them to the 212 pages whose images live on someone else’s CDN. Of 200 thumbnails, 4 did not resolve: three whose dimensions were not inside the first 8 KB, and one that timed out. Writing 1920 × 1080 on all 200 would mean publishing a number we had not verified about a remote file that can change without telling us. Leaving the tags off costs nothing, because platforms measure the image once they fetch it.

Our own card’s tags are safe for a different reason: the build reads them out of the PNG header at generation time, so the declared number cannot drift from the asset. Which is exactly the failure mode the CrazyGames row above is a live example of.

The underlying principle is the same one that governs every other number we publish: a dimension you have not measured is a claim, not a fact.

Why was it the hand-written pages again?

All 17 of our blog posts carried the wide wordmark. That is not a coincidence and it is not carelessness — it is the same structural hole that had just cost us three untagged pages, described in an earlier post about missing analytics tags.

The mechanism, in one sentence: a hand-authored page starts life as a copy of an older page’s <head>, so it inherits whatever that head contained — and the generator deliberately never overwrites a file a human wrote, so the template can never reach in and correct it. Every hand-written page is a snapshot of your head element on the day it was created.

The repair is a build step rather than a checklist item, because a checklist still depends on somebody remembering on file number eighteen. It is deliberately narrow: it only touches a page whose og:image is one of our own logo files. Give a post a real cover image of its own and it is left completely alone.

A gap we found while writing this, and have not closed. All 17 posts now serve the new card as their og:image — but the BlogPosting structured data on those same 17 pages still names the 1695 × 387 wordmark as its image. Two tags on one page, disagreeing about what the page’s image is. The repair covered the one we went looking at and not the one beside it. We are writing it down rather than quietly fixing it later, because the whole argument of this article is that the assumption is the expensive part: we assumed a fix aimed at og:image had covered the page’s images. It had not. This page uses the card in both.

What is worse than a cropped preview?

A missing one. A cropped image looks like a glitch; a preview with no image at all looks like a dead link, and it is the exact failure you invite when you repoint every page at a brand-new file that has not shipped yet.

So the card is protected three separate ways, none of which trusts the others:

  1. The build throws a fatal error if the card is missing from source, before it writes anything.
  2. The generator copies the card into the output as part of the build rather than relying on someone to remember it.
  3. The bundle assembler asserts that every same-host og:image resolves to a real file in the bundle before it can be shipped.

And each of those assertions was proved able to fail — run against a deliberately broken copy of the site to confirm it catches the break, then against the clean one to confirm it still passes. A check that has never failed is not a check; it is a line of code you feel good about.

How do you check yours in five minutes?

  1. Read your own markup from outside. curl -s https://yoursite.com/ | grep -o 'og:image[^>]*' — and note your twitter:card value in the same pass, because the two only make sense together.
  2. Measure the file, not the CSS. Fetch the first 16 KB and read the header. For a PNG that is four bytes at a known offset:
import urllib.request, struct
req = urllib.request.Request(IMG_URL, headers={"Range": "bytes=0-16383"})
b = urllib.request.urlopen(req).read()
w, h = struct.unpack('>II', b[16:24])      # PNG IHDR
print(w, h, round(w / h, 3))
  1. Divide. Anything outside roughly 1.7 – 2.1 will visibly crop in a wide card. A wordmark will be somewhere around 4. A square logo will be 1.0.
  2. Check the image resolves from outside your network, on a clean request. A 404 here is worse than a bad ratio.
  3. If you declare width and height, confirm they match the file. They drift, as the table above shows.
  4. Then do your second-most-shared page type, not just the homepage. This is the step that actually pays. Ours was fine on 212 pages and broken on 23 — and the 23 were the ones anybody shares.
  5. Control your own probe. Point the same script at a URL you know is not an image and at one you know does not exist. If it returns a confident answer for both, it is not measuring anything.

That last one is not padding. The reason this defect lived on our site for as long as it did is that everything we had ever pointed at it reported success.

What we could not verify

Related reading

Making something playable?

SlowDen is a place to put a browser game where people can find it and play it — no download for the player, no store page for you. Games here come from creators, partners and third-party sources, and each one is credited to whoever made it.

Slow Cook is our creative challenge round. You get a prompt, you build something around it, you submit it for consideration. Selected entries have a chance to be featured, and selected winners may receive a shoutout across SlowDen’s social accounts. That is the honest list; there is nothing guaranteed beyond it. Check the Slow Cook page for the current round and its closing date.

Submit a browser gameSee the Slow Cook challenges

Questions people ask

What size should an og:image be?

1200 x 630 pixels is the conventional size, because it is close to the 1.91:1 shape that a summary_large_image card renders at. The exact pixel count matters far less than the ratio: what breaks a preview is declaring a wide card and supplying an image that is not wide-card shaped. If you declare the smaller summary card instead, a roughly square image is the right choice. Match the image to the card type you actually declare, and measure the file rather than trusting the filename.

Why is my link preview cropped?

Almost always because the image's aspect ratio does not match the card type declared in the markup. Nothing in the pipeline rejects a wrongly shaped image. The platform accepts it, fits it to the card's fixed shape, and throws away whatever does not fit. There is no error, no warning and no console message, which is exactly why the defect survives for years. Ours was a 4.38:1 wordmark inside a 1.91:1 card, a 2.3x mismatch, so more than half the width was cut on every share.

Can I use my logo as an og:image?

Only if the logo file is roughly the shape of the card you declare. A horizontal wordmark is the worst possible case, because wordmarks are extremely wide. Ours measured 1695 x 387, which is 4.38:1, against a card that renders at about 1.91:1. The result is a centre slice of the wordmark with the first and last letters cut off. A purpose-built card at the card's own ratio, carrying the wordmark scaled proportionally inside it, fixes this without touching the logo asset.

Should I set og:image:width and og:image:height?

Set them if you have measured that exact file, and leave them out if you are guessing. The tags let a platform lay out the preview before it has fetched the image, which is a real benefit. But a declared dimension that does not match the file is a published claim that is untrue, and remote images can change without telling you. We left them off 212 pages whose images are hosted by someone else and could not all be measured, and we generate them for our own card by reading the number out of the PNG header at build time so it cannot drift.

Is a game screenshot better than a generic brand card?

For a page about one specific game, usually yes. A preview showing the actual game tells a reader what they are about to click; a brand card tells them the domain, which the platform already prints underneath. That is why we deliberately left 212 of our 235 pages alone. Their og:image is the game's own thumbnail at 1920 x 1080, which is 16:9 and crops to a wide card almost losslessly. Replacing those with a generic card would have been a downgrade dressed up as a fix.

I fixed the markup and the preview is still wrong. What now?

Platforms cache their scrape of your page, sometimes for a long time, so a corrected preview usually needs the cache cleared before it will show. Force a re-scrape through the platform's own card or sharing debugger and look again before you conclude the markup is still wrong. Editing the page a second time on the strength of a cached preview is how a fixed defect turns into a week of chasing. Check separately that the image URL itself returns 200 from outside your network, because a missing image renders blank rather than wrong.