Skip to content

Commit c437598

Browse files
therealalephclaude
andcommitted
fix(exit_node): strip Content-Encoding + Content-Length on response (#964)
@mehdimalekidev reported `Content Encoding Error` when ChatGPT was routed through Apps Script + exit-node. Root cause: 1. Browser → Apps Script with `Accept-Encoding: gzip, br` (default). 2. Apps Script forwards header to exit-node. 3. exit-node calls `fetch(destination)` which **auto-decompresses** the response body (Deno / Bun / Node `fetch()` does this by default). 4. `resp.arrayBuffer()` returns **plain decompressed bytes** but `resp.headers` still has `Content-Encoding: gzip` from the destination. 5. exit-node forwards both — Apps Script + Rust client pass them through — browser sees `Content-Encoding: gzip` on plain bytes → "Content Encoding Error: invalid or unsupported form of compression". Fix: strip `Content-Encoding` and `Content-Length` from the response headers before returning to the relay. The Apps Script + Rust transport layer reframes the wire body anyway, so neither header is meaningful to forward end-to-end. Affects ChatGPT (gzip), Claude (br), Reddit (gzip), and any other compressed exit-node-routed destination — the fix makes them all work. No new test (this fixes a regression that would only show in a real fetch path; mocking auto-decompression behavior is fragile). Manual verification: tested ChatGPT through exit-node, response renders normally in Firefox. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 072a917 commit c437598

1 file changed

Lines changed: 11 additions & 0 deletions

File tree

assets/exit_node/exit_node.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,20 @@ export default async function (req: Request): Promise<Response> {
146146
redirect: "manual",
147147
});
148148

149+
// `fetch()` (Deno / Bun / Node) auto-decompresses gzip / br / deflate
150+
// responses, so `resp.arrayBuffer()` returns plain bytes — but the
151+
// destination's `Content-Encoding` header is still on `resp.headers`.
152+
// Forwarding it would tell the client browser "this body is gzipped"
153+
// when it isn't, producing `Content Encoding Error` (#964). Same goes
154+
// for `Content-Length` — the post-decompression byte count is
155+
// different from what the destination announced. Strip both. The
156+
// Apps Script + Rust transport layer below us re-frames the wire body
157+
// anyway, so neither header is meaningful to forward.
149158
const data = new Uint8Array(await resp.arrayBuffer());
150159
const respHeaders: Record<string, string> = {};
151160
resp.headers.forEach((value, key) => {
161+
const lower = key.toLowerCase();
162+
if (lower === "content-encoding" || lower === "content-length") return;
152163
respHeaders[key] = value;
153164
});
154165

0 commit comments

Comments
 (0)