Worker fails on repeated view (Chrome only)


#1

When my worker is active, repeated page-views fail in the sense that Cloudflare returns an empty page (content-length: 0).

Surprisingly:

  • This behaviour happens with Chrome, but not Firefox.
  • It happens since an unknown time, but got the first report today. But I haven’t changed my Worker script in months. Not sure why the Worker script is not robust anymore.

Here’s what happens. The original page request goes well, and Chrome returns these headers:

cache-control: public, immutable, max-age=2700
cf-cache-status: HIT
cf-h2-pushed: </assets/css/category.min.a6851018b9047d3d521f3c02afeff786.css>
cf-ray: 4b773159ab34c869-AMS
content-encoding: br
date: Thu, 14 Mar 2019 15:13:39 GMT
etag: W/"4b3e7a10c0bd7da210525a15d8984cdf"
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
last-modified: Fri, 08 Mar 2019 16:01:50 GMT
link: </assets/css/category.min.a6851018b9047d3d521f3c02afeff786.css>; rel=preload; as=style
origin-server: eu-frankfurt
referrer-policy: strict-origin-when-cross-origin
server: cloudflare
status: 200
strict-transport-security: max-age=31536000; includeSubDomains; preload
vary: Accept-Encoding
x-content-type-options: nosniff
x-xss-protection: 1; mode=block

Notice that there’s a cache-control; I cache static HTML.

But when the browser’s page is already in cache, refreshing the page fails and Chrome returns:

cache-control: public, immutable, max-age=2700
cf-cache-status: HIT
cf-ray: 4b7733e24e72c869-AMS
content-length: 0
date: Thu, 14 Mar 2019 15:15:22 GMT
etag: "4b3e7a10c0bd7da210525a15d8984cdf"
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
last-modified: Fri, 08 Mar 2019 16:01:50 GMT
origin-server: eu-frankfurt
server: cloudflare
status: 200
strict-transport-security: max-age=31536000; includeSubDomains; preload
vary: Accept-Encoding
x-content-type-options: nosniff

The odd thing is:

  • My server sets a content-length header and my Worker doesn’t change/remove that header. Yet now it got a value of 0.
  • My server sets a content-type header and my Worker doesn’t change/remove that header. Yet now that header is gone.

Things I explored myself:

  • The problem does not happen when I load & refresh a page from my server (so without going through Cloudflare).
  • It also doesn’t happen when I disable the Worker.
  • My Worker success rate is 100%, with no failed requests. So as far as the Worker is concerned everything works fine.

This problem seems like the bug fixed in October, except that now it doesn’t happen with HEAD requests but with GET request.

Who might have an idea what’s going on? :confused:


(I didn’t open a support ticket yet since there’s no Workers category anymore as a ticket option. So I thought I should ask here, as opposed to having my ticket bounce around first.)


#2

Hi @JuM,

It’s hard to say exactly what’s going on without seeing your code. However, my guess is that this has to do with 304 responses.

Probably Chrome is sending a request with If-Modified-Since or If-None-Match, to say: “I already have a copy of this page in cache; only send me a response if it has changed.” Your worker probably forwards this to your origin. When the origin (or maybe Cloudflare’s cache) determines that the page has not changed, it sends back a “304 Not Modified” response with no body. My guess is that somewhere in your worker, you’re losing track of this response code, and replacing it with a 200. However, the body is still empty.

Could this be the reason?


#3

Thanks for the quick reply!

That’s completely right, I didn’t do that yet. :facepalm: Thanks for your good observation! :slight_smile:

Previously, I did:

if (!contentType.startsWith("text/html")) {
    return new Response(response.body, {
        headers: newHeaders,
        method: request.method
    });
}

But that had to be:

if (!contentType.startsWith("text/html")) {
    return new Response(response.body, {
        headers: newHeaders,
        method: request.method,
        status: response.status,
    });
}

I have three additional questions if you don’t mind:

  • Should I return all properties from the Response interface? I don’t see the Worker examples in the docs do that. But in this case it was needed.

  • Do you know good resources (besides the official doc and Cloudflare blog posts)? I want to prevent this kind of mistake in the future.

  • Do you have tips on testing a Worker script? I thought I tested everything, but something slipped through the cracks obviously. :slight_smile:


#4

Unfortunately the standard APIs for modifying requests and responses are kind of awkward.

When modifying response headers, I suggest this pattern:

// Make a new response object that is an exact copy of the original.
// (Note, do NOT use clone() here! It does something different.)
response = new Response(response.body, response);

// Now that you made a copy, the headers can be changed.
response.headers.set("foo", "bar");
return response;

The awkwardness of the interface is unfortunately just the way the standard is. We have considered adding a better but non-standard interface for these kinds of rewrites, but obviously we’re hesitant to go off-spec too much…

Sorry, I don’t have any specific tips beyond what’s in the docs.