Skip to content

fix(sri): re-hash public assets after modules have populated them - #759

Open
BobTheShoplifter wants to merge 1 commit into
Baroshem:mainfrom
BobTheShoplifter:fix/sri-hash-public-assets-after-population
Open

BobTheShoplifter wants to merge 1 commit into
Baroshem:mainfrom
BobTheShoplifter:fix/sri-hash-public-assets-after-population

Conversation

@BobTheShoplifter

Copy link
Copy Markdown

Problem

SRI hashes are computed on nitro:build:before, but public asset directories are still being filled at that point.

@nuxt/fonts writes zero-byte placeholder files while Vite resolves the CSS, and only downloads the real font data during rollup:before:

// @nuxt/fonts
context.callback = (filename) => writeFileSync(join(cacheDir, filename), '')   // placeholder
// ...later, in nitro's rollup:before
await fsp.writeFile(join(cacheDir, filename), res)                             // real bytes

So every /_fonts/* entry in the generated table ends up as the SHA-384 of an empty string:

sha384-OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb

That is harmless until something emits a <link> for those assets. Setting preload: true in the fonts config does exactly that, and fonts are a destination Chromium does enforce SRI on, so every font is rejected:

Failed to find a valid digest in the 'integrity' attribute for resource '/_fonts/鈥off2'

Every real FontFace ends up in error state and the site renders entirely in fallback fonts.

Fix

Hash again on rollup:before, after modules have finished writing, and merge. The second pass corrects the placeholder entries; merging keeps everything the first pass already resolved, which some builds depend on.

Verified

On a real Nuxt 4 site with @nuxt/fonts and preload: true:

before after
Font entries with the empty-string hash 3 of 6 0 of 6
Hash matches the file on disk no all 6
Distinct integrity values across 3 preload links 1 3
Real font faces in error state all 0

yarn lint clean and all 18 test files pass.

Related

#655 covers the same surface but a different cause: whether SRI belongs on preload/modulepreload links at all. This one is about the hash value itself being wrong. Fixing either would hide this symptom for fonts, but the empty hashes would remain in the table.

@vercel

vercel Bot commented Aug 30, 2026

Copy link
Copy Markdown

@BobTheShoplifter is attempting to deploy a commit to the Baroshem's projects Team on Vercel.

A member of the Team first needs to authorize it.

Public asset directories are still being filled when `nitro:build:before`
fires. @nuxt/fonts writes zero-byte placeholder files while Vite resolves
the CSS and only downloads the real font data on `rollup:before`, so the
existing pass hashes empty files and records the SHA-384 of an empty string
for every font.

That is harmless until something emits a link for those assets. Setting
`preload: true` in the fonts config does exactly that, and because fonts
are a destination Chromium enforces SRI on, every font is rejected and the
whole site falls back to system fonts.

Hashing again on `rollup:before` and merging picks up the real bytes while
keeping everything the first pass already resolved.
@BobTheShoplifter
BobTheShoplifter force-pushed the fix/sri-hash-public-assets-after-population branch from 1bb3220 to 8ddf2ec Compare August 30, 2026 15:59

This branch has not been deployed

No deployments
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.

1 participant