🐛 The bug
proxy-handler.ts forwards client request headers to the upstream target but never strips hop-by-hop headers. The request loop only skips host, SENSITIVE_HEADERS, and (conditionally) content-length:
// packages/script/src/runtime/server/proxy-handler.ts (main @ 90aced6ae)constlowerKey=key.toLowerCase()// host header — fetch derives it from URLif(lowerKey==='host')continue// SENSITIVE_HEADERS always stripped regardless of privacy flagsif(SENSITIVE_HEADERS.includes(lowerKey))continueif(lowerKey==='content-length'){/* ... */}Hop-by-hop headers (connection, keep-alive, proxy-authenticate, proxy-authorization, te, trailer, transfer-encoding, upgrade) are connection-specific. Per RFC 7230 §6.1 a proxy must not forward them. Forwarding them onto the upstream fetch() can corrupt the upstream exchange (mis-framed bodies when a stale transfer-encoding/content-length rides along, broken keep-alive negotiation, dropped requests), and the failure mode varies by upstream and by the runtime's HTTP stack.
The response side already handles this via SKIP_RESPONSE_HEADERS (line 28), so the request side is just missing the symmetric filter.
I might be missing a reason these are intentionally forwarded. Happy to be corrected.
🛠️ To reproduce
Proxy any registry script (firstParty / proxy) from a client that sends hop-by-hop request headers. Browsers and in-app webviews routinely send Connection: keep-alive, and some clients send TE / Transfer-Encoding. Those headers reach the proxied fetch() and get forwarded upstream. Whether it surfaces depends on the upstream and the Nitro HTTP stack. In our case it disrupted Sentry proxying and a native (iOS webview) network signal until we stripped them.
🌈 Expected behavior
The proxy strips the standard hop-by-hop set on the request side, mirroring SKIP_RESPONSE_HEADERS:
constSKIP_REQUEST_HEADERS=newSet(['connection','keep-alive','proxy-authenticate','proxy-authorization','te','trailer','transfer-encoding','upgrade'])// in the request loop, right after the host check:if(SKIP_REQUEST_HEADERS.has(lowerKey))continue
ℹ️ Additional context
We hit this proxying analytics and Sentry through the first-party proxy. Forwarding connection / transfer-encoding regressed Sentry proxying and a native network signal, and we are carrying exactly the filter above as a local patch today.
Happy to send a PR, since it mirrors the existing SKIP_RESPONSE_HEADERS pattern.
Environment: @nuxt/scripts@1.1.1 (also present on main @ 90aced6), Nitro on Vercel (Node 22).
🐛 The bug
proxy-handler.tsforwards client request headers to the upstream target but never strips hop-by-hop headers. The request loop only skipshost,SENSITIVE_HEADERS, and (conditionally)content-length:Hop-by-hop headers (
connection,keep-alive,proxy-authenticate,proxy-authorization,te,trailer,transfer-encoding,upgrade) are connection-specific. Per RFC 7230 §6.1 a proxy must not forward them. Forwarding them onto the upstreamfetch()can corrupt the upstream exchange (mis-framed bodies when a staletransfer-encoding/content-lengthrides along, broken keep-alive negotiation, dropped requests), and the failure mode varies by upstream and by the runtime's HTTP stack.The response side already handles this via
SKIP_RESPONSE_HEADERS(line 28), so the request side is just missing the symmetric filter.I might be missing a reason these are intentionally forwarded. Happy to be corrected.
🛠️ To reproduce
Proxy any registry script (
firstParty/proxy) from a client that sends hop-by-hop request headers. Browsers and in-app webviews routinely sendConnection: keep-alive, and some clients sendTE/Transfer-Encoding. Those headers reach the proxiedfetch()and get forwarded upstream. Whether it surfaces depends on the upstream and the Nitro HTTP stack. In our case it disrupted Sentry proxying and a native (iOS webview) network signal until we stripped them.🌈 Expected behavior
The proxy strips the standard hop-by-hop set on the request side, mirroring
SKIP_RESPONSE_HEADERS:ℹ️ Additional context
We hit this proxying analytics and Sentry through the first-party proxy. Forwarding
connection/transfer-encodingregressed Sentry proxying and a native network signal, and we are carrying exactly the filter above as a local patch today.Happy to send a PR, since it mirrors the existing
SKIP_RESPONSE_HEADERSpattern.Environment:
@nuxt/scripts@1.1.1(also present onmain@ 90aced6), Nitro on Vercel (Node 22).