Using Cache API with gzipped R2 object

Hi!

I am trying to use the Cache API for a response that contains a gzip encoded html file (persisted in R2) as the response body.

The Worker uses a custom domain.

The problem is, when trying to return the body as a
ReadableStream
(see code below), cache match stops returning anything and the full code evaluates every time.

I have verified that the Cache works as expected when I decompress the body before returning it as the response body.

The code looks roughly like this (some business logic removed for brevity):

export interface Env {
  R2: R2Bucket;
}

interface CustomExportedHandler extends Omit<ExportedHandler, "fetch"> {
  fetch: ExportedHandlerFetchHandler<Env, unknown>;
}

const handler: CustomExportedHandler = {
  async fetch(request, { R2 }, ctx): Promise<Response> {
    try {
      const url = new URL(request.url);

      const cacheKey = `https://${url.hostname}${url.pathname}`;
      const cacheMaxAge = 60 * 15; // 15 min
      const cache = caches.default;

      const cachedPage = await cache.match(cacheKey);

      let response = new Response(cachedPage?.body, cachedPage);
      // Set Cache-Control headers (otherwise default from Cloudflare is 4h)
      response.headers.set("Cache-Control", `s-maxage=${cacheMaxAge}`);

      if (!response) {
        const r2Object = await R2.get(url.pathname);
        if (!r2Object) throw Error("No page data.");

        response = new Response(r2Object.body, {
          ...response,
          encodeBody: "manual",
        });
        response.headers.set("content-encoding", "gzip");
        response.headers.set("content-type", "text/html;charset=utf-8");

        ctx.waitUntil(cache.put(cacheKey, response.clone()));
      }
      return response;
    } catch (e: unknown) {
      if (e instanceof Error) {
        console.error(`Error requesting: ${request.url}\n -> ${e.message}`);
      }
      return new Response("Not found", { status: 404 });
    }
  },
};

export default handler;
Was this page helpful?