OG image engineering: size, bytes, format, redirects
Someone pastes your launch URL into WhatsApp. Slack shows a crisp card. WhatsApp shows nothing. The meta tags look perfect in View Source. The debugger for another platform still works. So you re-export a “sharper” PNG at 1200×630, push again — and WhatsApp is still blank.
That is not a branding problem. That is image engineering: the bytes, MIME type, hop chain, and auth model of og:image under real crawlers.
Fault tree: four different “no image” stories
Treat symptoms as separate branches. Mixing them wastes days.
| Symptom | Likely branch | First check |
|---|---|---|
| No image on every platform | Tag / URL / reachability | Raw HTML has absolute https://… og:image; curl -sI returns 200 |
| Old image after you replaced the file | Cache / cache key | Same URL → platforms keep the previous scrape (rescrape; series post on cache) |
| Broken / soft / cropped art | Dimensions / aspect / declared size | Real pixels ≥ ~1200×630, ~1.91:1, safe zone inset |
| Only one client fails (often WhatsApp / chat) | Bytes / format / hops | Payload size, JPEG vs giant PNG, redirect depth, Content-Type vs magic |
The blank-card checklist covers relative URLs, JS-only tags, and crawler blocks. This post stays on the image resource itself once the tag is present.
Size and aspect ratio (necessary, not sufficient)
Industry default for large link previews is still roughly 1200×630 (~1.91:1). Facebook / Meta, LinkedIn, and X publish recommended heights in that band (630 / 627 / 628). For engineering purposes, treat them as one banner slot: design on 1.91:1, put critical faces and copy inside a ~10% inset, and accept center-crop.
<meta property="og:image" content="https://cdn.example.com/share/launch.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Product dashboard on a dark background" />
Rules of thumb that match how scanners grade you:
- Below recommended size → soft or upscaled cards (
og-image-dimensions-too-small). - Far from 1.91:1 → crop risk on feed layouts (
og-image-aspect-ratio-crop-risk). - Declared width/height ≠ decoded pixels → some clients trust meta and layout badly (
og-image-declared-size-mismatch). - Missing
og:image:alt→ accessibility and some fallbacks (og-image-alt-missing).
If your only export is a 400×400 logo, no amount of JPEG quality will make a large banner card look intentional.
Bytes and format (where IM clients go silent)
Pixels tell designers a story. Bytes tell chat apps whether to bother.
@vercel/og, Satori, and many “dynamic OG” paths default to PNG. At 1200×630 a PNG is often hundreds of KB to multi-MB. That is fine for a marketing site hero; it is hostile for some unfurl paths.
| Format | When it helps | When it hurts |
|---|---|---|
| JPEG | Photos, gradients, product shots; best “default share” format | Text + flat UI can look muddy if quality is too low |
| WebP | Smaller than JPEG at similar quality where supported | Not every client treats it equally; keep a JPEG path if you must maximize compatibility |
| PNG | Sharp UI chrome, logos, screenshots with hard edges | Payload explodes; chat previews often drop it first |
| GIF | Rare motion / legacy | Large, limited; not a default for product launches |
Byte budgets are platform-specific and often unofficial. In OGKit’s platform profiles, WhatsApp is modeled with a ~300KB hard / ~200KB soft budget, source marked measured / community consensus — not an official guarantee. Other profiles allow multi-megabyte limits (Meta/Facebook-class order of magnitude ~8MB, several others ~5MB) with preferred formats listed per platform. Treat every hard number as operational guidance, not a contract. Re-measure if a client changes behavior.
Practical target for a share card that must work in chat apps:
- Export 1200×630.
- Encode JPEG quality ~80 (or WebP if you control fallbacks).
- Aim for ~100–300KB total when WhatsApp / IM matters.
- If you generate PNG in edge functions, transcode before you put the URL in
og:image.
The rule that surfaces this in scans is og-image-bytes-exceeds-platform-budget.
Content-Type must match the bytes
Crawlers are picky about lying headers. If the file is JPEG magic but Content-Type: image/png (or vice versa), some clients reject the image even though your browser displays it from <img>.
# Headers the crawler sees
curl -sI "https://cdn.example.com/share/launch.jpg" | rg -i 'HTTP/|content-type|content-length|location'
# Optional: confirm magic vs extension (install file(1) or use a hex dump of the first bytes)
curl -sL "https://cdn.example.com/share/launch.jpg" | head -c 16 | xxd
Stable rule: og-image-content-type-mismatch. Fix the origin or CDN mapping — not the meta tag alone.
Redirects, signed URLs, and 403 after “it worked in the browser”
Your browser follows cookies, multi-hop 302s, and marketing short links. Social image fetchers are logged-out, hop-budgeted clients.
Common failure patterns:
Short link → auth wall → CDN
og:imagepoints athttps://link.example/xwhich 302s through a tracker to a signed object URL. By the time a slow crawler arrives, the signature expired → 403, blank card. Rule:og-image-forbidden.Too many hops
Some crawlers follow zero or few redirects on image URLs even if they follow redirects on the HTML page. Rule:og-image-redirects.Cookie / IP allowlists
Staging CDNs that “work for the team VPN” fail for every public unfurl.
Fix: put the final public CDN object URL (HTTPS, 200, no cookies, long-lived or immutable) directly in og:image. Do not make crawlers finish your redirect graph.
# See hop chain without following forever
curl -sI -L --max-redirs 5 -A "facebookexternalhit/1.1" \
"https://example.com/og/launch.png"
If the final URL differs from the meta value, update the meta. If any hop requires auth, stop using that chain for share images.
Also keep HTTPS absolute URLs: og-image-relative-url, og-image-not-https.
Executable triage order (do this in order)
When a card “sometimes” loses its image, run this sequence — stop when you find a hard fail.
Tag present in raw HTML?
curl -sL URL | rg -i 'og:image'
No → SSR / injection problem (other posts in the series).Absolute HTTPS?
Relative orhttp://→ fix URL before touching design.Final image GET as a crawler
curl -sI -A "facebookexternalhit/1.1" IMAGE_URL
Expect 200, correctContent-Type, no auth.Redirect budget
CountLocationhops. Prefer zero on the image URL.Pixels
Decode real width×height; compare to 1200×630 and 1.91:1; fix declared meta if present.Bytes
CompareContent-Length(or actual size) to the strictest client you care about. For chat-heavy launches, re-encode under a few hundred KB rather than debating theoretical multi-MB caps.Format honesty
Magic vsContent-Typevs extension must agree.Platform-only stale card
If steps 1–7 are green but one network still shows an old asset, that is cache, not image engineering — use official rescrape paths.
What scanners already encode as rules
| Rule id | Severity (typical) | Catches |
|---|---|---|
og-image-missing |
error | No image field |
og-image-relative-url |
error | Relative path |
og-image-not-https |
error | Non-HTTPS |
og-image-forbidden |
error | 401 / 403 / auth |
og-image-content-type-mismatch |
error | MIME ≠ magic |
og-image-redirects |
warn | Redirecting image URL |
og-image-dimensions-too-small |
warn | Below ~1200×630 |
og-image-aspect-ratio-crop-risk |
warn | Far from 1.91:1 |
og-image-declared-size-mismatch |
warn | Meta size ≠ pixels |
og-image-bytes-exceeds-platform-budget |
warn | Over platform byte budget |
og-image-alt-missing |
warn | No alt |
Full catalogue: rule reference. Per-platform notes and preferred formats: platform profiles.
Ship checklist for a share image
- Dedicated export ≥ 1200×630, ~1.91:1, critical content in the safe zone
- JPEG (or carefully tested WebP) under the strictest relevant byte budget; avoid multi-MB PNG for chat
-
og:imageis the final HTTPS CDN URL that returns 200 anonymously -
Content-Typematches file magic;og:image:width/heightmatch reality if you set them -
og:image:altis a short plain-language description - After swapping art, rescrape platforms that still show the old card
Try it on a live URL
Paste a production page into the homepage scan. Image findings show up as named rules — dimensions, budgets, redirects, forbidden responses — not a single “looks fine” mock card. Use that report to drive the triage order above, then fix the origin once instead of re-exporting PNGs forever.
Next in the series: cache TTL and official rescrape (why a correct image URL still shows last week’s campaign), then dynamic OG generation paths where PNG defaults come back to haunt you.