🐛 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)
const lowerKey = key.toLowerCase()
// host header — fetch derives it from URL
if (lowerKey === 'host')
continue
// SENSITIVE_HEADERS always stripped regardless of privacy flags
if (SENSITIVE_HEADERS.includes(lowerKey))
continue
if (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:
const SKIP_REQUEST_HEADERS = new Set(['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).