-
Notifications
You must be signed in to change notification settings - Fork 78
Description
Environment
- @nuxt/scripts version: 1.0.0-beta.1 (built from commit b0a5dca on main, PR feat: first-party proxy mode with privacy anonymization #577)
- Mode:
firstParty: { privacy: 'proxy' }
Description
Two bugs in the firstParty proxy implementation:
1. Missing path separator in proxy URL construction
Both the beacon intercept plugin and the SW template construct proxy URLs by concatenating rule.target + pathWithoutPrefix, but when pathPrefix is / (e.g., Meta Pixel at connect.facebook.net/en_US/fbevents.js), the stripped path loses its leading /:
pathPrefix = "/"
pathname = "/en_US/fbevents.js"
pathWithoutPrefix = pathname.slice(pathPrefix.length) = "en_US/fbevents.js" // no leading /
proxyUrl = "/_proxy/meta" + "en_US/fbevents.js" = "/_proxy/metaen_US/fbevents.js" // WRONG
Expected: /_proxy/meta/en_US/fbevents.js
Affected locations:
module.mjs~line 1175 (beacon intercept plugin)runtime/sw/proxy-sw.template.js~line 39 (SW fetch intercept)
Fix: Add a separator:
const separator = pathWithoutPrefix.startsWith('/') ? '' : '/';
const proxyUrl = rule.target + separator + pathWithoutPrefix + parsed.search;Note: rewriteScriptUrls in runtime/utils/pure.js correctly uses joinURL() for the same operation, so this is only a bug in the two intercept paths.
2. Registry scripts with static src (no scriptBundling) are never bundled
Scripts like Meta Pixel and Reddit Pixel define src in their registry entry but don't have a scriptBundling function:
// registry.mjs - Meta Pixel
{
proxy: "metaPixel",
src: "https://connect.facebook.net/en_US/fbevents.js",
// no scriptBundling
}In the AST bundle transformer (NuxtScriptBundleTransformer), registryNode.src is only checked in the guard at line ~496 but never used as a fallback source for bundling:
// Line 496: guard passes because registryNode.src is truthy
if (!registryNode.scriptBundling && !registryNode.src) return;
// Line 513-518: but src is only set from scriptBundling, never from registryNode.src
if (!scriptSrcNode) {
src = registryNode.scriptBundling && registryNode.scriptBundling(mergedOptions);
// registryNode.src is never used as fallback!
}
// Line 520: both are falsy → early return, bundling skipped entirely
if (!scriptSrcNode && !src) { return; }Result: The script loads from the third-party URL at runtime. The SW must intercept it, but the SW isn't active on first page load → script only loads through proxy on refresh, not on initial visit.
Fix: Add registryNode.src as fallback after line 517:
if (!src && registryNode.src)
src = registryNode.src;This makes scripts with static src get properly bundled at /_scripts/hash.js with proxyRewrites applied to their content, removing the SW dependency for initial script loading.
Reproduction
- Configure
scripts: { firstParty: { privacy: 'proxy' } }withmetaPixelin registry - Deploy to Vercel
- Observe
/_proxy/metaen_US/fbevents.js(404) in network tab instead of/_proxy/meta/en_US/fbevents.js - Observe that Meta Pixel script is NOT bundled at
/_scripts/— it tries to load from the third-party URL - On first page load, SW isn't active → script loads directly from
connect.facebook.net(bypassing proxy entirely) - On refresh, SW intercepts but constructs wrong proxy URL (bug 1)