Skip to content
Back to Blog
Performance10 min read

Common Cloudflare Cache Rules Mistakes: 9 Fixes That Work

Cache rules look simple until they break your site. Here are the nine configuration mistakes I see most often in support tickets and how to fix each one.

Written by Abdul AbrorTechnical Hosting Support Engineer
Common Cloudflare Cache Rules Mistakes: 9 Fixes That Work
On this page

Cache rules in Cloudflare promise faster page loads and lower origin server bills. Then you deploy them and cache hit rates stay near zero, or worse—your site starts serving stale checkout pages.

I've handled dozens of tickets where cache rules were technically valid but functionally broken. The dashboard accepts the config, analytics show green, yet the origin server still gets hammered. Most of these failures trace back to nine recurring mistakes.

Mistake 1: Caching URLs with query strings by default

Cloudflare treats example.com/page and example.com/page?utm_source=email as different URLs. Many cache rules fail because they don't account for query string variations.

The default behavior ignores query strings entirely when deciding what to cache. If your site uses query parameters for tracking (utm tags, fbclid, gclid), each variant creates a separate cache entry. Your hit rate drops because the same page gets cached dozens of times.

The fix: Use the "Cache Key" settings to normalize query strings. In your cache rule, add a custom cache key that sorts query parameters alphabetically or ignores specific tracking params:

Cache Key settings:
- Query string: Include specific parameters (id, page, category)
- Ignore: utm_source, utm_medium, utm_campaign, fbclid, gclid

For static assets like CSS and JavaScript, ignore all query strings. They're often just cache-busting timestamps.

Mistake 2: Setting cache TTL longer than origin headers allow

You set "Edge Cache TTL" to 7 days in your rule. Analytics still show most requests hitting origin. Check your origin server's response headers—if it sends Cache-Control: max-age=300, Cloudflare respects that.

Cache rules don't override origin cache headers unless you explicitly tell them to. The "Respect origin" setting means exactly that. Your 7-day TTL sits unused while the origin's 5-minute directive runs the show.

The fix: Either fix the origin headers or enable "Override origin" in the cache rule. I prefer fixing origin headers when possible because it keeps caching logic in one place:

# Apache .htaccess
<FilesMatch "\.(jpg|jpeg|png|gif|css|js|woff2)$">
  Header set Cache-Control "public, max-age=604800"
</FilesMatch>

If you can't touch origin config, use the "Edge Cache TTL" rule and set it to override. Just document why you're doing it.

Mistake 3: Creating overlapping rules without understanding order

Cache rules execute top to bottom. The first matching rule wins, then Cloudflare stops processing.

I've seen setups where a broad rule like "cache everything from /assets/" sits above a specific rule for "/assets/user-uploads/". The user uploads rule never fires. Private images get cached and served to the wrong visitors.

Order matters brutally here.

The fix: Put specific rules before general ones. Drag the user-uploads rule above the assets rule. Think of it like firewall rules or Apache directives—most specific match first:

1. Don't cache: /assets/user-uploads/*
2. Cache 7 days: /assets/*
3. Cache 1 hour: /*

Test by checking response headers for URLs that match multiple patterns. The CF-Cache-Status header tells you which rule won.

Mistake 4: Bypassing cache for logged-in users everywhere

A rule like "if cookie contains 'wordpress_logged_in', bypass cache" seems safe. It is safe. It also kills performance for your entire admin team and any user who stays logged in.

The issue isn't correctness—it's scope. Applying this rule to all requests means logged-in users never hit edge cache, even for public CSS files, JavaScript libraries, and images that don't change based on login state.

The fix: Scope the bypass rule narrowly. Only skip cache for URLs that actually differ by user:

Rule 1 (order: first):
If cookie contains "wordpress_logged_in" AND URI path starts with "/wp-admin"
Then bypass cache

Rule 2:
If URI path matches ".*\.(css|js|jpg|png|woff2)"
Then cache, ignore all cookies

Static assets get cached for everyone. Dynamic pages get personalized. Your logged-in users load faster and origin load drops.

So what if cache hit rates look good but content stays stale?

High cache hit rates feel great until a customer reports seeing yesterday's pricing. This is mistake five.

Mistake 5: No purge strategy when content changes

You set aggressive cache TTLs to maximize hits. Pages stay cached for days. Then you publish an urgent update and it doesn't appear for hours because edge servers serve the cached version.

Cache rules without purge automation create a choice between performance (long TTL) and freshness (short TTL). You shouldn't have to choose.

The fix: Implement cache purging in your deployment pipeline. Cloudflare's API supports purging by URL, prefix, tag, and hostname.

For WordPress sites, plugins like WP Cloudflare Super Page Cache handle this automatically. For custom apps, add a purge call after publishing:

# Purge specific URLs after deploy
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \
  -H "Authorization: Bearer API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"files":["https://example.com/page","https://example.com/api/data"]}'

Or use cache tags in origin responses and purge by tag. Tags let you purge all product pages at once without listing every URL.

Mistake 6: Caching API responses with user-specific data

A cache rule matches /api/* and sets a 5-minute TTL. Seems reasonable—API responses should be fast. Then users start seeing each other's data.

API endpoints often return different content for different users based on authentication tokens, session cookies, or IP address. A single cached response gets served to everyone.

The fix: Don't cache authenticated API endpoints by default. If an API response truly is public and identical for all users, cache it. Otherwise, bypass:

Rule for /api/*:
If request header "Authorization" exists OR cookie "session_id" exists
Then bypass cache

For public API endpoints that do vary by parameter (like /api/posts?category=tech), make sure query strings are part of the cache key as described in mistake one.

Mistake 7: Forgetting about Vary headers

Your origin returns Vary: Accept-Encoding so browsers get gzipped or brotli versions based on what they support. You cache the response. Half your visitors get the wrong encoding.

Cloudflare handles Accept-Encoding automatically, but other Vary headers aren't considered by default. If your site returns Vary: User-Agent or Vary: Accept-Language, cached responses might not match the visitor's context.

The fix: Either remove unnecessary Vary headers at origin or understand that Cloudflare will cache one version and serve it to everyone. For mobile vs. desktop, use responsive design instead of server-side detection. For language, put language in the URL path (/en/page vs /es/page) rather than relying on Accept-Language headers.

The goal is to make URLs fully describe what you're serving. Cache keys work with URLs, not headers.

Mistake 8: Enabling cache without Browser Cache TTL

You set Edge Cache TTL to 7 days. Browser Cache TTL stays at the default (4 hours). Repeat visitors still re-download assets because their browser cache expired.

Edge caching speeds up requests that reach Cloudflare. Browser caching prevents requests from leaving the device at all. You want both.

The fix: Set Browser Cache TTL in the same rule where you set Edge Cache TTL:

Cache rule for static assets:
- Edge Cache TTL: 7 days
- Browser Cache TTL: 7 days

For content that changes occasionally, use shorter browser TTL (1 day) with longer edge TTL (7 days). You can purge edge cache instantly but can't force browsers to refresh.

Mistake 9: Using "Cache Everything" without understanding POST requests

The "Cache Everything" page rule or cache level setting sounds perfect for maximum performance. Then form submissions start failing or return cached responses.

Cloudflare only caches GET and HEAD requests by default. "Cache Everything" extends this to include responses that would normally bypass cache, but it still doesn't cache POST, PUT, DELETE, or other HTTP methods. However, it can cache HTML pages that contain forms, and if those pages get cached with CSRF tokens, the tokens become stale.

The fix: Never use "Cache Everything" on paths that handle form submissions or user interactions. Apply it only to truly static sections:

Cache Everything rule:
- URI path starts with "/blog"
- AND URI path does not start with "/blog/contact"

Better yet, skip "Cache Everything" entirely and build specific rules for each content type. It's more work but far less likely to cache something that should stay dynamic.

What to check first when cache rules fail

Start with response headers. Open DevTools, load a page, and look for CF-Cache-Status. The value tells you exactly what happened:

  • HIT: Served from cache
  • MISS: Not in cache, fetched from origin
  • EXPIRED: Was in cache but TTL expired
  • BYPASS: Cache rule explicitly bypassed
  • DYNAMIC: Content is uncacheable by default

If you see BYPASS when you expect HIT, a rule earlier in the list is catching the request. Check rule order. If you see MISS on the second request to the same URL, check if query strings or cookies are varying the cache key.

Run a quick audit:

  1. Check rule order—most specific first
  2. Verify Edge Cache TTL isn't being overridden by origin
  3. Confirm query strings are normalized or ignored
  4. Make sure cookies aren't varying cache for public content
  5. Check that Vary headers are necessary and handled
  6. Test with curl to see raw headers without browser caching

Most cache problems trace back to these nine mistakes. Fix them and cache hit rates climb.

Does Cloudflare cache HTML by default?

No. Cloudflare caches static assets like images, CSS, and JavaScript by default but not HTML. You need a cache rule with "Cache Everything" or specific HTML file extensions to cache HTML pages.

How do I test if my cache rule is working?

Load the URL twice and check the CF-Cache-Status header in DevTools. The first request should show MISS, the second should show HIT. If both show BYPASS or DYNAMIC, the rule isn't matching or something is preventing caching.

Can I cache different versions for mobile and desktop?

You can, but it's rarely needed with responsive design. If you must, use a cache key that includes the User-Agent header. Just know that this multiplies your cache entries and lowers hit rates.

What's the maximum Edge Cache TTL I can set?

Free plans allow up to 2 hours by default for certain file types. Paid plans support much longer TTLs. Check your plan limits in the dashboard. Even if you can cache for months, consider purge strategy first.

When cache rules make sense

Cache rules work when you match TTL to content change frequency, purge proactively, and keep rules simple and ordered. They fail when you cache user-specific content, ignore origin headers, or apply blanket rules without testing.

I've seen a single well-configured cache rule cut origin load by 80%. I've also seen five overlapping rules cause more problems than they solved. Start with one rule for your most-requested content type, verify it works, then expand.

The goal is higher cache hit rates without serving stale or wrong content. Every one of these nine mistakes works against that goal.