Skip to content

fix(cloudflare): resolve fonts via localFetch when ASSETS binding unavailable#527

Merged
harlan-zw merged 4 commits intomainfrom
fix/cloudflare-wsa-font-resolution
Mar 23, 2026
Merged

fix(cloudflare): resolve fonts via localFetch when ASSETS binding unavailable#527
harlan-zw merged 4 commits intomainfrom
fix/cloudflare-wsa-font-resolution

Conversation

@harlan-zw
Copy link
Copy Markdown
Collaborator

🔗 Linked issue

Closes #523

❓ Type of change

  • 📖 Documentation
  • 🐞 Bug fix
  • 👌 Enhancement
  • ✨ New feature
  • 🧹 Chore
  • ⚠️ Breaking change

📚 Description

On Cloudflare Workers with Workers Static Assets (WSA), the ASSETS service binding is not injected into the worker's env object. Static assets are served at the Cloudflare runtime level before the worker's fetch runs, so env.ASSETS is undefined. This differs from Cloudflare Pages where ASSETS is always present in env.

The previous fallback used event.$fetch (ofetch) which cannot serve public assets on CF Workers, returning 404 for all font files. This caused blank OG images with no visible text.

Replaces the event.$fetch fallback with event.fetch (Nitro localFetch) which routes through the h3 app and Nitro's built-in public asset handler, correctly resolving fonts from the asset manifest.

Reproduction: Clone https://github.com/yusufalitangoz/nuxt-4-shadcn-boilerplate, build with NITRO_PRESET=cloudflare_module, run with wrangler dev. OG images render blank. With this fix, fonts load and text appears.

…vailable

On Cloudflare Workers with Workers Static Assets (WSA), the ASSETS
service binding is not injected into the worker's env object. Static
assets are served at the runtime level before the worker's fetch runs,
so env.ASSETS is undefined.

The previous fallback used event.$fetch (ofetch) which routes through
Nitro's external HTTP path and cannot serve public assets, returning
404 for font files. This caused all fonts to fail loading, resulting
in blank OG images with no visible text.

Replace the event.$fetch fallback with event.fetch (Nitro localFetch)
which routes through the h3 app including Nitro's built-in public
asset handler. This correctly resolves fonts from the asset manifest.

Closes #523
When preferStatic is set (Takumi), the font loader was using satoriSrc
which points to .woff files downloaded by fontless for Satori. Takumi
only supports .ttf and .woff2, so these .woff files loaded successfully
but failed at render time with "Unsupported font format".

Now only uses satoriSrc when it's a format Takumi can parse (.ttf),
otherwise falls through to the original .woff2 which Takumi handles
natively.
…mats

Replace the boolean `supportsWoff2` and `preferStatic` options with a
single `supportedFormats` set that explicitly declares which font
formats each renderer can parse:

  Satori: ttf, woff (cannot parse WOFF2)
  Takumi: ttf, woff2 (cannot parse WOFF)

This eliminates .woff fonts being loaded and rejected by Takumi at
render time ("Unsupported font format" warnings), and removes the
`preferStatic` workaround which was causing Takumi to use .woff
satoriSrc files it couldn't parse.

Also gates fontless downloads (WOFF2 → static TTF/WOFF conversion)
on hasSatoriRenderer() only, since Takumi handles WOFF2 natively
and doesn't need fontless alternatives. This reduces build time and
output size for Takumi-only projects.
Satori supports TTF, OTF, and WOFF. The previous commit missed OTF.
@harlan-zw harlan-zw merged commit c850dfd into main Mar 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: Title and description are not showing in production.

1 participant