Can the use of Cloudflare Worker negatively impact website performance?

I’m wondering if using Cloudflare Worker could negatively impact the response time in the following scenario.

Let’s consider this simple worker:

async fetch(request: Request): Promise<Response> {
	const response = await fetch(request);
    const headers = new Headers(response.headers);
    headers.append("Set-Cookie", someCookie);

    const newResponse = new Response(response.body, {
      status: response.status,
      statusText: response.statusText,
      headers: headers,
    });
    return newResponse;
},

This worker simply sets a cookie to the response. I’m wondering if the worker waits to receive the entire response before modifying the headers and transferring it to the client. If so, the time it takes to transfer the response from the origin server to the client through the Cloudflare Worker could be much longer than not using the worker.
This is because a response can be divided into many packets. If we only use Cloudflare as a reserve proxy (without using a worker), each packet would be sent to the client as soon as it arrives at the proxy. However, when we add a worker as middleware, the worker needs to wait for the entire response to be modified before sending it to the client. This means that while receiving the response from the origin server, the worker will not send any data to the client at all, potentially doubling the response time in the worst-case scenario.

Yes, by around 15 ms.

No, the fetch() promise resolves when the response headers are returned. The actual body is streamed.

If you are simply setting a cookie I would recommend using Transform Rules instead of Workers.

1 Like

@albert Thanks for your very quick reply.

What happened if we want to replace some text in the response? How the worker can stream the response and replace the text at the same time? Another case is that we fetch data from 2 APIs. The worker absolutely needs to wait for two responses returned completely and then process the next step, right?

const text = (await response.text()).replaceAll('A', 'B')
return new Response(text)

Yes, this would buffer the response in memory. If the body is HTML you can use HTMLRewriter · Cloudflare Workers docs to rewrite while streaming the response.

So the worker needs to wait until it receives the full response before modifying it, right? Sorry for the seemingly simple question, but I am concerned about the impact on performance rather than the implementation details.

As I understand it, the worker will stream the response to the client, but it first needs to wait for all the await calls in its code to return the entire response before it can build it up. This waiting time will add up and may impact the response time.

Let’s consider this worker

const response1 = await fetch(url1);
const response2 = await fetch(url2);
return new Response(response1.text().split(';')[0] +response2.text().split(';')[0]  )

The worker has to wait for response1 and response2 resolved completely before returning its response right?

So, fetch() returns a Promise which you can await. The promise resolves when Cloudflare receives the response headers from the origin server. Therefore, if you do return await fetch(request), the added delay is very minimal (~15 ms from my experience).

If you do

const response = await fetch(request) // Resolves when the Worker receives the response headers
const text = await response.text() // Resolves when the Worker has received and buffered the entire response body
return new Response(text)

it will have a noticeable impact on performance, as the Worker will not pass through the response body stream.

That is correct. You can speed it up a bit awaiting multiple Promises in parallel

const [response1, response2] = await Promise.all([
    fetch(url1),
    fetch(url2)
])


const [text1, text2] = await Promise.all([
    response1.text(),
    respons2.text()
])

return new Response(text1.split(';')[0] + text2.split(';')[0])

but the Worker still has to buffer the entire response body in memory before returning a response to the client.

If both response1 and response2 have small bodies (<1 MB), I would not be too concerned about performance.

So if I do not read nor modify the body, the worker will pass through the response body stream. If I do one of the actions above, the worker has to buffer the entire response body before returning a response to the client. That’s all I want to figure out. Thank you so much

Correct. response.body is a ReadableStream. If you return new Response(response.body), the Workers runtime can pass through the body stream. If you call response.text(), response.json(), response.arrayBuffer() etc., you force it to buffer the entire body in memory.

1 Like

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.