fix(web): decompress .gz objects in Pages Function instead of setting Content-Encoding

Cloudflare CDN strips Content-Encoding: gzip from Worker responses even when
explicitly set, leaving browsers with raw gzip bytes they cannot parse as JSON.
Fix by piping .gz objects through DecompressionStream('gzip') inside the Worker
so the response body is always plain JSON — no Content-Encoding header needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-04-30 12:51:28 -04:00
parent ea8318bc10
commit 651f344247

View file

@ -26,11 +26,14 @@ export const onRequest: PagesFunction<Env> = async (context) => {
headers.set('Cache-Control', 'public, max-age=60');
headers.set('Access-Control-Allow-Origin', '*');
// R2 binding strips Content-Encoding when serving object body, even when
// the object was stored with ContentEncoding metadata. Re-apply it so
// browsers know to decompress gzipped objects (.json.gz, .gz).
if (key.endsWith('.gz') && !headers.has('Content-Encoding')) {
headers.set('Content-Encoding', 'gzip');
// Cloudflare CDN strips Content-Encoding: gzip from Worker responses even
// when we set it, causing browsers to receive raw gzip bytes as JSON.
// Decompress .gz objects inside the Worker instead so the response body is
// always plain, browser-parseable content (no Content-Encoding needed).
if (key.endsWith('.gz')) {
headers.delete('Content-Encoding');
const decompressed = object.body.pipeThrough(new DecompressionStream('gzip'));
return new Response(decompressed, { headers });
}
return new Response(object.body, { headers });