Skip to content

Commit 1fb03eb

Browse files
authored
Merge pull request #487 from thefrontside/fix-proxy-gzip-header
🐛 stop proxy from serving decompressed HTML as gzip
2 parents 3f43189 + 331f3b9 commit 1fb03eb

1 file changed

Lines changed: 38 additions & 14 deletions

File tree

routes/proxy-route.ts

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ export interface ProxyRouteOptions {
1818
}
1919

2020
export function proxyRoute(options: ProxyRouteOptions): HTTPMiddleware {
21-
let middleware: HTTPMiddleware & SitemapExtension = function* proxy(request): Operation<Response> {
21+
let middleware: HTTPMiddleware & SitemapExtension = function* proxy(
22+
request,
23+
): Operation<Response> {
2224
let website = new URL(options.website);
2325

2426
let target = new URL(request.url);
@@ -41,10 +43,7 @@ export function proxyRoute(options: ProxyRouteOptions): HTTPMiddleware {
4143
if ([301, 302, 307, 308].includes(response.status)) {
4244
let location = response.headers.get("location");
4345
if (location?.startsWith(String(website))) {
44-
let headers: Record<string, string> = {};
45-
for (let [key, value] of response.headers.entries()) {
46-
headers[key] = value;
47-
}
46+
let headers = copyHeaders(response);
4847

4948
let url = new URL(request.url);
5049

@@ -101,20 +100,18 @@ export function proxyRoute(options: ProxyRouteOptions): HTTPMiddleware {
101100
posixNormalize(`${base.pathname}${url}`),
102101
);
103102
} else if (properties.content.startsWith("http")) {
104-
properties.content = properties.content.replace(target.origin, base.href.replace(/\/?$/, ''));
103+
properties.content = properties.content.replace(
104+
target.origin,
105+
base.href.replace(/\/?$/, ""),
106+
);
105107
}
106108
}
107109
}
108110
}
109-
let headers: Record<string, string> = {};
110-
for (let [key, value] of response.headers.entries()) {
111-
headers[key] = value;
112-
}
113-
114111
response = new Response(toHtml(tree), {
115112
status: response.status,
116113
statusText: response.statusText,
117-
headers,
114+
headers: copyHeaders(response),
118115
});
119116
}
120117

@@ -123,7 +120,10 @@ export function proxyRoute(options: ProxyRouteOptions): HTTPMiddleware {
123120

124121
if (options.prefix) {
125122
middleware.sitemapExtension = function* (): Operation<RoutePath[]> {
126-
let sitemap = new URL(`/${options.root ?? ""}sitemap.xml`, options.website);
123+
let sitemap = new URL(
124+
`/${options.root ?? ""}sitemap.xml`,
125+
options.website,
126+
);
127127
try {
128128
let response = yield* call(() => fetch(sitemap));
129129
if (!response.ok) return [];
@@ -148,6 +148,28 @@ export function proxyRoute(options: ProxyRouteOptions): HTTPMiddleware {
148148
return middleware;
149149
}
150150

151+
// Copy an upstream response's headers, dropping the ones that describe how the
152+
// *original* body was framed on the wire. `fetch()` transparently decompresses
153+
// the body, so by the time we rebuild the response the payload is plain text —
154+
// carrying over the upstream `content-encoding` (e.g. gzip) or its stale
155+
// `content-length` makes us serve uncompressed bytes labelled as gzip, which
156+
// downstream clients then fail to decode ("Invalid gzip header"). Let the
157+
// server recompute these for the new body.
158+
function copyHeaders(response: Response): Record<string, string> {
159+
let skip = new Set([
160+
"content-encoding",
161+
"content-length",
162+
"transfer-encoding",
163+
]);
164+
let headers: Record<string, string> = {};
165+
for (let [key, value] of response.headers.entries()) {
166+
if (!skip.has(key.toLowerCase())) {
167+
headers[key] = value;
168+
}
169+
}
170+
return headers;
171+
}
172+
151173
function parseSitemapUrls(
152174
xml: string,
153175
options: ProxyRouteOptions,
@@ -159,7 +181,9 @@ function parseSitemapUrls(
159181
let loc = match[1];
160182
try {
161183
let url = new URL(loc);
162-
let path = options.root ? url.pathname.replace(`/${options.root}`, "/") : url.pathname;
184+
let path = options.root
185+
? url.pathname.replace(`/${options.root}`, "/")
186+
: url.pathname;
163187
let pathname = posixNormalize(`/${options.prefix}${path}`);
164188
paths.push({ pathname });
165189
} catch {

0 commit comments

Comments
 (0)