Skip to content
OCM LABS

How to apply Cloudflare cache rules for Magento (and write them by hand)

Apply the six OCM Labs Cloudflare cache rules for Magento from the admin or CLI with a dry-run diff, or paste the exact expressions by hand.

Shane Blandford
Shane Blandford · FOUNDERPUBLISHED · 7 MIN READ

Cloudflare will not cache your Magento HTML until a cache rule tells it to. Out of the box the edge caches static file types and passes everything else to the origin, so cf-cache-status: DYNAMIC on a category page is the default state, not a bug. You need a rule that makes storefront HTML eligible for cache, plus bypass rules under it so checkout, account pages and POSTs never land in the edge cache.

The tempting shortcut is one "Cache Everything" rule. It is the wrong rule: it ignores Magento's private, no-store headers, so checkout and account pages get cached and served to the next visitor. The rules below respect origin headers, so Magento's Cache-Control still decides what is cached and for how long.

OCM Labs Cloudflare, a free and open source Magento module, writes six Cloudflare cache rules for Magento to your zone, shows a diff first, and documents the expressions so you can paste them yourself.

Step 1: Check the token and the ACL

Applying needs an API token with Zone:Cache Rules:Edit (separate from Zone:Settings:Edit, and the one people miss) and, from the admin, the OCMLabs_Cloudflare::settings_write ACL resource. If you cannot add Zone:Cache Rules:Edit to the token, skip to step 3.

Step 2: Apply from the admin or the CLI

From the admin, open System > Cloudflare > Dashboard > Health and click Apply recommended cache rules. From the shell:

bin/magento ocmlabs:cloudflare:rules:apply --dry-run
bin/magento ocmlabs:cloudflare:rules:apply

Run the dry run first. It prints the diff the apply would make:

  • Kept: rules that stay as they are. Your own rules always land here.
  • Added: [OCMLabs] rules the zone does not have yet.
  • Changed: [OCMLabs] rules whose expression or actions differ from what the module wants.
  • Removed: [OCMLabs] rules the current settings no longer need, for example the vary cookie bypass after switching Vary Handling to Cache key.

Read the kept list for a note saying one of your rules sits above the module's rules and might override them. Put your own rules below the OCMLabs rules unless you mean the override.

Run the real apply once the diff looks right. Running it twice changes nothing the second time.

How the naming keeps websites apart

The module only touches rules whose description starts with [OCMLabs]. Each name ends with the Magento website code, for example [OCMLabs] Cache storefront HTML (base), so several websites can share one zone: applying the rules for one website leaves the other websites' [OCMLabs] rules, and all of your own, alone.

The six rules in order

Cloudflare evaluates cache rules top to bottom, and a later match overrides an earlier one for each setting it sets. So the bypass rules have to sit below the cache rule, and the "Bypass rules" health check confirms they do. This is the order the module writes.

RULEWHAT IT DOES
1Cache storefront HTMLEverything on the storefront hosts is eligible for cache. Edge TTL and browser TTL respect the origin headers.
2Static assets/static/ and /media/ are cached for a year at the edge and in the browser. Magento versions static URLs and image variants live at their own paths, so this is safe.
3Bypass protected pathsEvery path in Bypass Paths, plus the ___store= and ___from_store= store switch URLs.
4Bypass non-GETAnything that is not a GET request.
5Bypass on vary cookieRequests carrying the X-Magento-Vary cookie. Only written when Vary Handling is Bypass.
6Bypass query stringsRequests with a query string, except static and media paths. Only written when Cache Query Strings is No.

Rules 5 and 6 depend on the Edge Cache settings. Change Vary Handling or Cache Query Strings and the next dry run shows the rule being added or removed.

The Enterprise cache key variant

With Vary Handling set to Cache key on an Enterprise zone, rule 5 is replaced by a custom cache key on rule 1 that includes the X-Magento-Vary cookie, so store views, currencies and customer groups are cached as separate variants. On any other plan the module falls back to the bypass rule and says so in the preview.

Step 3: Write the rules by hand

Use this when the token lacks Zone:Cache Rules:Edit, or to see exactly what the module writes. In Cloudflare go to your zone > Caching > Cache Rules > Create rule. For each rule choose Edit expression, paste the expression and set the actions listed.

Before you paste:

  • Replace www.example.com with your storefront host. For several hosts, list them inside the braces separated by spaces: {"www.example.com" "example.com"}.
  • Replace /admin in rule 3 with your real admin path (the frontName in app/etc/env.php).
  • Rule 3 uses the default Bypass Paths. If you changed that list, change the expression to match. Each path needs both the eq term and the starts_with term.
  • Name each rule exactly as shown, starting with [OCMLabs] and ending with your website code in brackets. If you later grant the token Zone:Cache Rules:Edit and run rules:apply, the module takes over those rules for that website and leaves everything else alone.

1. [OCMLabs] Cache storefront HTML (base)

(http.host in {"www.example.com"})
  • Cache eligibility: Eligible for cache
  • Edge TTL: Use cache-control header if present (respect origin)
  • Browser TTL: Respect origin TTL
  • Enterprise with Vary Handling set to Cache key only: Cache key > Cookie > include X-Magento-Vary, and skip rule 5

2. [OCMLabs] Static assets (base)

(http.host in {"www.example.com"}) and (starts_with(http.request.uri.path, "/static/") or starts_with(http.request.uri.path, "/media/"))
  • Cache eligibility: Eligible for cache
  • Edge TTL: Ignore cache-control header and use this TTL, 1 year (31536000 seconds)
  • Browser TTL: Override origin, 1 year (31536000 seconds)

3. [OCMLabs] Bypass protected paths (base)

Same pattern for every bypass path: an eq term for the bare path and a starts_with term for everything under it, on lower(http.request.uri.path). A final group bypasses query strings containing ___store= or ___from_store=, the store switch URLs.

(http.host in {"www.example.com"}) and ((lower(http.request.uri.path) eq "/checkout" or starts_with(lower(http.request.uri.path), "/checkout/") or lower(http.request.uri.path) eq "/customer" or starts_with(lower(http.request.uri.path), "/customer/") or lower(http.request.uri.path) eq "/sales" or starts_with(lower(http.request.uri.path), "/sales/") or lower(http.request.uri.path) eq "/wishlist" or starts_with(lower(http.request.uri.path), "/wishlist/") or lower(http.request.uri.path) eq "/paypal" or starts_with(lower(http.request.uri.path), "/paypal/") or lower(http.request.uri.path) eq "/rest" or starts_with(lower(http.request.uri.path), "/rest/") or lower(http.request.uri.path) eq "/graphql" or starts_with(lower(http.request.uri.path), "/graphql/") or lower(http.request.uri.path) eq "/admin" or starts_with(lower(http.request.uri.path), "/admin/") or lower(http.request.uri.path) eq "/downloadable" or starts_with(lower(http.request.uri.path), "/downloadable/") or lower(http.request.uri.path) eq "/newsletter/subscriber" or starts_with(lower(http.request.uri.path), "/newsletter/subscriber/") or lower(http.request.uri.path) eq "/review/product/post" or starts_with(lower(http.request.uri.path), "/review/product/post/") or lower(http.request.uri.path) eq "/catalogsearch/advanced/result" or starts_with(lower(http.request.uri.path), "/catalogsearch/advanced/result/") or lower(http.request.uri.path) eq "/contact" or starts_with(lower(http.request.uri.path), "/contact/")) or (http.request.uri.query contains "___store=" or http.request.uri.query contains "___from_store="))
  • Cache eligibility: Bypass cache

4. [OCMLabs] Bypass non-GET (base)

(http.host in {"www.example.com"}) and (http.request.method ne "GET")
  • Cache eligibility: Bypass cache

5. [OCMLabs] Bypass on vary cookie (base)

Only when Vary Handling is Bypass.

(http.host in {"www.example.com"}) and (http.cookie contains "X-Magento-Vary=") and (not (starts_with(http.request.uri.path, "/static/") or starts_with(http.request.uri.path, "/media/")))
  • Cache eligibility: Bypass cache

6. [OCMLabs] Bypass query strings (base)

Only when Cache Query Strings is No.

(http.host in {"www.example.com"}) and (http.request.uri.query ne "") and (not (starts_with(http.request.uri.path, "/static/") or starts_with(http.request.uri.path, "/media/")))
  • Cache eligibility: Bypass cache

Save the rules in this order, top to bottom. The full reference is on the Cache Rules docs page.

Step 4: Verify with the health checks

Run the checks, or click Run checks now on the Health tab:

bin/magento ocmlabs:cloudflare:health

The Cache rules group confirms a [OCMLabs] Cache storefront HTML rule exists for the website's hosts, the bypass rules exist and sit below it, the X-Magento-Vary cookie is handled by the bypass rule or an Enterprise cache key, a rule bypasses non-GET requests, and the edge TTL matches the module's. That last check warns when a rule overrides the origin TTL. Do not add a "Cache Everything" rule or set an edge TTL override on rule 1: either one makes Cloudflare ignore private, no-store and cache checkout and account pages.

Then check the edge by hand:

curl -sI https://www.example.com/some-category.html | grep -i cf-cache-status
curl -sI https://www.example.com/customer/account/login/ | grep -i cf-cache-status

The first shows HIT from the second request onward once edge caching is on. The second shows DYNAMIC or BYPASS. All 25 checks, including the live probes behind these two curls, are in how to run Cloudflare health checks for Magento.

The rules alone cache nothing. Rule 1 respects origin headers, so until Enable Edge Caching of HTML is on, the category page stays DYNAMIC. Turning that on and tuning bypass paths, vary and query strings is the next step: how to cache Magento HTML at the Cloudflare edge.

If you would rather have someone check the zone and the rules for you, that is part of our performance and infrastructure work.

FAQ

Why does Cloudflare not cache my Magento pages without a cache rule?+
By default Cloudflare only caches static file types and passes HTML to the origin. A rule marking the storefront hosts as eligible for cache lets HTML in, and Magento's Cache-Control headers then decide which pages are stored.
Why not just use a Cache Everything rule?+
Cache Everything makes Cloudflare ignore the private, no-store headers Magento sends on checkout and account pages, so those pages get cached and served to other visitors. Rule 1 respects origin headers instead, and the bypass rules below it keep protected paths and POSTs out.
What does the dry run show?+
The diff the apply would make: rules kept, added, changed and removed. Your own rules always appear as kept, with a note if they sit above the module's rules and might override them.
Will applying the rules touch rules I wrote myself?+
No. The module only touches rules whose description starts with the OCMLabs prefix. Every other rule in the zone stays exactly where it is, and the diff lists it as kept.
How do several Magento websites share one Cloudflare zone?+
Each rule name ends with the Magento website code in brackets, so each website has its own set. Applying the rules for one website leaves the other website's OCMLabs rules untouched.
Which health checks confirm the rules are right?+
The Cache rules group checks that the storefront cache rule exists, the bypass rules sit below it, the vary cookie is handled, non-GET requests bypass the cache and the edge TTL matches the module's. It warns when a rule overrides the origin TTL.
Shane Blandford

Shane Blandford

FOUNDER

Founder of Orange Collar Media, a Denver ecommerce agency behind 800+ Magento and Shopify builds. In Magento since version 1.x - building, rescuing, and supporting revenue-critical stores since 2008.

All posts by Shane Blandford→

KEEP READING

Related from the Journal.

Your store can sell more. Let's find out how much.

Start a project