Skip to content

Proxy forwards hop-by-hop request headers to upstream (RFC 7230 §6.1) #791

Description

@felixgabler

🐛 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions