Return compressed response from worker

So I set up a static site with Workers Sites and I see the HTML and CSS files are being returned with br compression which is nice.

I tried doing the same returning some HTML from KV but the response is not being compressed.

const body = await NAMESPACE.get(key, 'arrayBuffer');
return new Response(body, {
	status: 200
});

If I use text instead of arrayBuffer the response is gzipped but obviously the browser is not interpreting the text correctly.

I skimmed over the source for kv-asset-handler which is what is being used for the static site and it seems the idea is to put the response in the CF cache which in turn will deliver the compressed response.

Is this the recommended approach to serve compressed responses?

That didn’t work. I’m fetching the response from the cache and getting a CF-Cache-Status: HIT header but still not getting gzip or brotli.

Edit:

To be clear I’m using the Cache API with cache.match(...) and cache.put(...).

So I got it working by simply doing:

response.headers.set('Content-Encoding', 'gzip');

No cache shenanigans.

It didn’t work when using https because the brotli compression was enabled on the domain dashboard, but still CF was’t serving the HTML with brotli. Go figure…

So I disabled brotli in the dashboard and now I can serve with gzip.

The only problem now is that I’d actually prefer to serve with brotli but for some reason request.headers.get('Accept-Encoding') only gets gzip and not the full Accept-Encoding value the browser is sending gzip, deflate, br. Not sure if this is a bug or I’m missing something.

Cloudflare doesn’t send through the actual content-encoding header the browser makes according to this: Cloudflare workers (fetch) removing content-encoding headers (gzip)

I too was struggling to get Cloudflare to return a compressed response, but I figured out that I was missing a Content-Type header on my response object, which means Cloudflare’s normal (not-workers-related) compression wasn’t kicking in. There’s a whitelist[^1] of Content-types that get compressed

Once I set a Content-type, Cloudflare’s normal brotli and gzip compression kicked in. I didn’t have to set a Content-encoding either, which I think is probably more correct since the worker isn’t actually returning a compressed response body.

TL;DR: Cloudflare’s normal brotli + gzip compression applies to workers the same as it does to other Cloudflare origins, but, your worker has to return a Content-type header in the whitelist[^1] for it to apply to a request.

[^1]: The whitelist: https://support.cloudflare.com/hc/en-us/articles/200168396-What-will-Cloudflare-compress- (sorry forum wouldn’t let me embed properly)

2 Likes

Thank you @harry.brundage that makes a lot of sense.

I’m going to try that!

Edit:

Yes it worked!

@harry.brundage I can’t seem to get the behaviour you describe:

Using this code:

    addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

/**
 * Respond to the request
 * @param {Request} request
 */
async function handleRequest(request) {
  return new Response('hello',
  { 
   headers: {
       'Content-type': 'text/html',
 },
    status: 200
  })
}

Then checking from command line like this:
curl -v -H 'Accept-Encoding: gzip' https://worker-url...
and the response is plaintext and missing a Content-Encoding: gzip header (meaning cloudflare didn’t automatically compress the response).