Skip to content

fix(proxy): relay bodies unencoded, since Next drops a handler's Content-Encoding - #130

Draft
jm-rivera wants to merge 1 commit into
devfrom
fix/proxy-content-encoding
Draft

jm-rivera wants to merge 1 commit into
devfrom
fix/proxy-content-encoding

Conversation

@jm-rivera

@jm-rivera jm-rivera commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Next 16.4.5, on dev since #120, strips content-encoding, content-length and transfer-encoding from every route-handler Response in sendResponse (next/dist/server/send-response.js; 16.4.4 has no such filter). src/lib/proxy/compress.ts gzipped relayed bodies and labelled them, so the bytes reached clients unlabelled and any client that accepts gzip, Node's fetch included, failed to parse the JSON. Next's own compression cannot take over: sendResponse copies headers with appendHeader, which stores Content-Type as an array, and the compression middleware skips a non-string type, so even /sitemap.xml goes out uncompressed.

The proxy therefore stops encoding bodies. compress.ts and its test are deleted, and forward() relays the body as fetch decoded it. Cloudflare compresses responses in front of production; staging's run.app host has no Cloudflare and serves the API uncompressed. Clients see chunked responses with no Content-Length, which Next strips whatever the proxy does. main is still on Next 16.3.0, so production is unaffected until the next release.

forward() reads the first chunk of every body except event streams and /mcp before the response goes out, so a body that fails before its first byte still gets a logged 502 problem document, as it did on the gzip path. Everything after the first chunk streams unread.

src/lib/proxy/test/forward.test.ts runs forward() against a local upstream that gzips: with and without Accept-Encoding: gzip the client gets no Content-Encoding and the decoded bytes; a body that fails before its first byte gets the 502; one that fails later gets a 200 whose body errors; an event stream reaches the client before the upstream ends. forward() imports server-only, @/ paths and extensionless modules, so the test loads it through resolve hooks in src/lib/test/serverModules.ts, registered with module.register. Once @types/node is at 22 or later, module.registerHooks can replace the two files with one call.

Closes #128

Testing: pnpm lint, pnpm lint:css, pnpm typecheck, pnpm test (2267 pass), pnpm build. The gzip and 502 tests fail on dev. On the built standalone server with a gzipping fake upstream, /api/v1/graph/schema and /openapi.json return plain JSON with no Content-Encoding, with and without Accept-Encoding: gzip, and Node fetch parses both.

scripts/share-migration-round-trip.ts passes on a build of this branch against the seeded dfo-apis local stack with no Accept-Encoding override, and proxied /api/v1/datasets responses arrive as plain JSON.

…ent-Encoding

Next 16.4.5 strips content-encoding, content-length and transfer-encoding from every route-handler Response in sendResponse, so the gzip compress.ts applied reached clients unlabelled, and Next's own compression skips route handlers. forward() now relays the body as fetch decoded it. Cloudflare compresses responses in front of production. A forward() test runs against a gzipping upstream with and without Accept-Encoding, loading the server module through resolve hooks in src/lib/test.

Closes #128

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.

Platform proxy drops Content-Encoding from gzipped responses

1 participant