stop asking for compression, so nothing has to unpick it

Every asset in the frame answered 502. `fetch()` decompresses transparently
while `upstream.headers` still claims content-encoding: gzip, so re-emitting
those headers with the decompressed body hands the next hop a lie and it dies
with ZlibError.

The HTML escaped it only because the sidecar deletes that header when it
rewrites — which is why the page rendered and every stylesheet, script and
image beside it failed.

Not asking for compression removes the mismatch rather than chasing it through
two hops. The cost is nil: this hop is loopback, and the browser's own
connection is compressed by whatever terminates TLS.
This commit is contained in:
officer
2026-08-19 09:16:54 +00:00
parent 61d43cd5f6
commit 21cb386e3d
+12
View File
@@ -79,6 +79,18 @@ router_.all('/_web/*', async (ctx) => {
for (const h of ['host', 'connection', 'keep-alive', 'transfer-encoding', 'upgrade', 'authorization']) {
headers.delete(h);
}
// ACCEPT-ENCODING GOES TOO, and this one is not tidiness.
//
// `fetch()` transparently decompresses a response, but `upstream.headers` still says
// `content-encoding: gzip`. Re-emitting those headers with the decompressed body hands the browser a
// lie, and it fails with ZlibError — reported here as a 502, which is what every asset in the frame
// was doing while the HTML rendered fine. The HTML escaped only because the sidecar drops that header
// when it rewrites.
//
// Asking for no compression at all removes the mismatch instead of chasing it through two hops. The
// cost is nil: this hop is loopback, and the browser's own connection to the platform is compressed by
// whatever terminates TLS.
headers.delete('accept-encoding');
// The sidecar binds loopback and trusts these, which is exactly why nothing but this may set them.
headers.set('X-Officer-User', String(userId));
// Where the browser thinks the frame lives. The sidecar rewrites Gitea's HTML, redirects and cookies