How do workers combine with page rules?

pagerules
caching

#1

Continuing the discussion from Cloudflare Workers Beta Feedback:

It’s unclear to me how Workers work in relation to Page Rules.

I ran into a situation today whereby a Page Rule was not applied to the request handled by a Worker.

The request matches this Page Rule:

  • https://example.com/*: cache level: cache everything, browser cache: 1 hour, edge cache: 1 month.

With that rule Cloudflare responds for my HTML with:

cache-control: public, max-age=3600

But for the URL that I’m beta testing my worker on, no cache-control header is added to the HTML response. Why is that?

My worker does perform a request to another domain, but if the browser visits https://example.com/my-page/, shouldn’t the Page Rule for that URL kick in? But in my case the Worker actually disables the Page Rule.


I understand from the following quote that Workers cannot set cache-control headers. That makes it more important for me that Page Rules do work, because otherwise there’s no caching happening with the Worker.


#2

Hi @JuM,

When it comes to combining other Cloudflare features with Workers, some features run “before” Workers (they run on the request before it reaches your Worker, or they run on the response returned by your Worker) while other features run “after” Workers (they operate on subrequests (fetch()es) made from your worker). (Note that the words “before” and “after” here are referring to where the feature sits in the pipeline between the user and your origin, not strictly to time of execution. Hence a feature that is “before” Workers, but operates on a response, actually executes after your worker has completed. Maybe it would be better to use the words “in front of” and “behind”…)

As a rule of thumb, “security” features (WAF, DDoS protection, CAPTHAs, etc.) run before Workers, while other features run after.

When it comes to Page Rules, whether the rule applies before or after Workers depends on what feature it is affecting.

In your case, you have set a Page Rule affecting caching. Caching runs after Workers. So, your page rule applies only to subrequests. But, it sounds like your subrequests are being pointed to a different domain, not your own. So, those subrequests do not match your page rule, since your page rule can only match your own domain.

Instead of using page rules, you can get the behavior you want by doing something like:

// Fetch the remote URL with "cache everything" and edge cache TTL of 1 month.
let response = await fetch(request, {cf: {cacheTtl: 2592000}})

// Clone response object so that we can modify it.
response = new Respnose(response.body, response)

// Set browser cache TTL of 1 hour.
response.headers.set("Cache-Control", "public, max-age=3600")

This achieves exactly what you described as your page rule, without a page rule.


#3

Thanks for the thoughtful and thorough reply! :slight_smile: I’ve implemented the approach you outlined in my Worker and it seems to be working fine.

One thing that confuses me a bit is that the expires header that’s automatically added by Cloudflare is always off when a Worker sets the Cache-Control header. Is that expected behaviour?

For example:

cache-control: public, immutable, max-age=2700
cf-cache-status: HIT
cf-ray: 42bc2b69dad055ac-ORD
content-security-policy: upgrade-insecure-requests
content-type: text/html; charset=utf-8
date: Sat, 16 Jun 2018 09:15:02 GMT
expires: Sat, 16 Jun 2018 10:15:02 GMT
server: cloudflare

2,700 seconds added to 9:15:02 doesn’t give me a time of 10:15:02.

Or this:

HTTP/2.0 200 OK
date: Sat, 16 Jun 2018 09:11:43 GMT
content-type: text/html; charset=utf-8
cache-control: public, immutable, max-age=2940
cf-cache-status: HIT
cf-ray: 42bc268fcf069cb3-AMS
expires: Sun, 17 Jun 2018 09:11:43 GMT

Here the expires header is 24 hour later even though the max-age is just 2,940.

My Worker nor my origin server sets an expires header. This is not a problem because expires is ignored when there’s a cache-control header according to MDN. But I just want to check if there’s no error hidden in my Worker/configuration before I set the worker to site-wide.