Skip to content

Commit

Permalink
Setting cache-control: max-age=31536000 with a Cloudflare Transform Rule
Browse files Browse the repository at this point in the history
  • Loading branch information
simonw authored Oct 24, 2024
1 parent 5256ea5 commit 4853ca1
Showing 1 changed file with 84 additions and 0 deletions.
84 changes: 84 additions & 0 deletions cloudflare/cache-control-transform-rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Setting cache-control: max-age=31536000 with a Cloudflare Transform Rule

I ran https://simonwillison.net/ through [PageSpeed Insights](https://pagespeed.web.dev/) and it warned me that my static assets were not being served with browser caching headers:

![A screenshot of a web performance report showing static asset cache policies. Header reads "Serve static assets with an efficient cache policy — 20 resources found" followed by a list of URLs from static.simonwillison.net showing file names, cache TTL status (mostly "None"), and transfer sizes ranging from 96 KiB to 8,871 KiB.](https://github.com/user-attachments/assets/115514c7-a51d-410b-a4dd-67c34084420d)

I serve static assets for my blog (mainly images) from `static.simonwillison.net`, which is an AWS S3 bucket served via Cloudflare.

I investigated with `curl -i`:

```bash
curl -I https://static.simonwillison.net/static/2024/prompt-gemini-extract.gif
```
```
HTTP/2 200
date: Wed, 23 Oct 2024 20:02:52 GMT
content-type: image/gif
content-length: 160263
x-amz-id-2: LpMSRQox/dMz/qS2p2jb6OmP7/YsEa4YZnOfEVgB5biGOPynNxqs8eY5EgWW0xzbclBnOI6LTAQ=
x-amz-request-id: BK8F63EJYFP5K1YB
last-modified: Wed, 23 Oct 2024 18:18:49 GMT
etag: "38a67c2deb66fe4b6f57796c2f2f5c44"
cf-cache-status: HIT
age: 149
accept-ranges: bytes
report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=xu4hL1Hr5W2ZDo6VSCd4NBfPp2tmCYiTNbct64mFfU6AAkBofX%2BHSZ6P%2FpIo3QKX5CqweeBhgS%2BYNOuzWW2E4pb4O3V1pISkOHzU0NPM%2B9QgGyuquDc%2FOl2aZM5HL3MjBPm0oKJD2AWWeyY%3D"}],"group":"cf-nel","max_age":604800}
nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
server: cloudflare
cf-ray: 8d7453c3481b9e5e-SJC
alt-svc: h3=":443"; ma=86400
server-timing: cfL4;desc="?proto=TCP&rtt=23918&sent=5&recv=9&lost=0&retrans=0&sent_bytes=2907&recv_bytes=585&delivery_rate=124913&cwnd=177&unsent_bytes=0&cid=ec38180c3ba3b0e7&ts=47&x=0"
```
That confirms it's being served by Cloudflare that Cloudflare itself is caching the page (`cf-cache-status: HIT`) - but there's no `cache-control` header in there telling my browser to cache the image. This means the image is fetched again from Cloudflare every time the page loads.

I wanted a quick way to add `cache-control: max-age=31536000` headers to every one of those resources. I already have a policy that I rename a file any time I upload a new version so I may as well cache everything in the browser for 31536000 (24 * 60 * 60 * 365 seconds aka a full year).

## Using Cloudflare Transform Rules

Cloudflare has a bewildering array of options these days. I asked Claude and it suggested using a "Transform Rule".

Here's the form I found in the Cloudflare dashboard for adding a new rule:

![A screenshot of a Cloudflare Transform Rules configuration page showing the creation of a new HTTP Response Header Modification Rule. The rule is named "Cache-Control: max-age=31536000" and is set to apply to requests where the hostname equals "static.simonwillison.net". The rule adds a cache-control header with max-age=31536000.](https://github.com/user-attachments/assets/42c0ab9f-5b78-4dfa-b684-aae2fe156e5f)

Cloudflare is configured for my entire `simonwillison.net` domain with the S3 bucket configured as a proxied caching CNAME to S3.

As such, I only wanted my rule to apply to `static.simonwillison.net`. I configured that using a custom filter expression of `Hostname = static.simonwillison.net`.

Then I told it to add a header name of `cache-control` and a value of `max-age=31536000` to all matching responses.

## Confirming it worked

I ran this again:
```bash
curl -I https://static.simonwillison.net/static/2024/prompt-gemini-extract.gif
```
And got back:
```
HTTP/2 200
date: Wed, 23 Oct 2024 20:07:12 GMT
content-type: image/gif
content-length: 160263
x-amz-id-2: LpMSRQox/dMz/qS2p2jb6OmP7/YsEa4YZnOfEVgB5biGOPynNxqs8eY5EgWW0xzbclBnOI6LTAQ=
x-amz-request-id: BK8F63EJYFP5K1YB
last-modified: Wed, 23 Oct 2024 18:18:49 GMT
etag: "38a67c2deb66fe4b6f57796c2f2f5c44"
cf-cache-status: HIT
age: 409
accept-ranges: bytes
report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=2HCHW9L52YEhmN%2FbP1ZVxQLL%2FtLrpf5GTN1J%2B8dUnvC53hhSyW2PDiAHv%2B43DvJnsUsnrInVAxdZCgTsIfQ7JCg98jFzHMdu6xb3uOGAHPdC1aE9utyUGvAedNa6RVxO9yx30qkUSuiwR2k%3D"}],"group":"cf-nel","max_age":604800}
nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
cache-control: max-age=31536000
server: cloudflare
cf-ray: 8d745a2099107ac4-SJC
alt-svc: h3=":443"; ma=86400
server-timing: cfL4;desc="?proto=TCP&rtt=23766&sent=5&recv=8&lost=0&retrans=0&sent_bytes=2907&recv_bytes=585&delivery_rate=131337&cwnd=117&unsent_bytes=0&cid=bd0475734101defd&ts=55&x=0"
```
Note the new `cache-control:` header in there.

I then tested it in Firefox. I loaded the page, opened up the network tab in browser DevTools and hit refresh. I got this:

![A screenshot of a browser network requests panel showing 18 HTTP requests for various resources including images, JavaScript, and icons. The requests are primarily to static.simonwillison.net, with most returning 200 status codes and "cached" transfer status. The page took 786ms to finish loading with DOMContentLoaded at 59ms.](https://github.com/user-attachments/assets/94b4eae4-c530-4379-b655-bea1f3689092)

Note the summary that says "1.38MB / 1.62 kB transferred" - the caching is working extremely well.

1 comment on commit 4853ca1

@simonw
Copy link
Owner Author

@simonw simonw commented on 4853ca1 Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.