Threads · note 04 / 4 · by Vorluno · Sep 21, 2026
Satori does not read woff2
Generated OpenGraph images fail silently when the font you serve to browsers is not a font the image renderer can parse. The fix is a second copy of the font — in a format the site never ships to users.
- #opengraph
- #fonts
- #next
The symptom
app/opengraph-image.tsx with Next's ImageResponse. Locally: 500, empty body, nothing in the console worth reading. The same component with the font lines removed: 200.
The cause
ImageResponse renders with Satori, and Satori parses TTF, OTF and WOFF — not WOFF2. Our fonts are self-hosted WOFF2 through next/font/local, which is right for the browser and useless for the renderer.
The fix that holds
Convert once, commit the result, read it from public/ so the standalone image has it too:
python -c "from fontTools.ttLib import TTFont; f=TTFont('cabinet-grotesk-700.woff2'); f.flavor=None; f.save('public/og/cabinet-grotesk-700.ttf')"
const cabinet = await readFile(join(process.cwd(), "public/og/cabinet-grotesk-700.ttf"));
return new ImageResponse(…, { width: 1200, height: 630, fonts: [{ name: "Cabinet", data: cabinet, weight: 700, style: "normal" }] });
Two details that cost us an hour each: fetch(new URL("./font.ttf", import.meta.url)) works in Next's docs and not in a shared package (the asset is not traced); and a dynamic route's opengraph-image.tsx takes params directly — generateImageMetadata is for several images of one page, and using it there produced one image per slug per page.
// next noteA stopped Lenis still eats the wheel