Configuring a robust Cloudflare CDN caching policy is one of the highest-impact engineering tasks for speed-optimising an enterprise website in 2026. Many web platforms suffer from high latency because every user request must travel to the origin database server to render pages. That origin dependence delays the First Contentful Paint (FCP) and Largest Contentful Paint (LCP) speed metrics, whereas storing static page layouts and asset components at edge locations worldwide delivers fast, low-latency responses from the nearest edge location. This guide breaks down the caching mechanics, Edge Cache TTL rules, and dynamic cookie bypass configurations.
[!TIP] Caching Optimization Tip: Avoid caching HTML pages that contain logged-in user details. Always configure your Cache Rules to bypass edge caching when specific session cookies (such as WordPress cookies or custom auth tokens) are detected in request headers.
Key Takeaways:
- Cloudflare CDN caching reduces backend server database queries, cutting down hosting costs.
- Cache Rules allow developers to set custom TTL values based on content types and directories.
- Deploying Cache Everything rules requires configuring session cookie bypasses to prevent user data leaks.
- Serving static pages directly from edge locations helps websites pass Core Web Vitals on mobile devices.
Caching Configurations: Page Rules vs. Cache Rules
To get the best out of your delivery pipelines, you must choose the appropriate dashboard control model. According to caching guidelines from Cloudflare Developer Docs , legacy Page Rules are being replaced by modular Cache Rules. Therefore, developers should implement these configurations:
By default, CDN networks cache only media formats, stylesheets, and scripts. Consequently, you must configure three core asset options:
- HTML Caching: To achieve instant page loads, you must instruct the CDN to cache the HTML document structure. Therefore, this stops database queries.
- Cache-Control Headers: Configure your Symfony or PHP backend server to send custom
s-maxagedirectives to instruct edge servers how long to store the pages. Additionally, this allows custom TTL rules. - Browser Cache TTL: Set shorter browser cache lifetimes (e.g. 4 hours) to ensure users receive updates when you modify site layouts. Consequently, this prevents layout mismatch issues.
For interactive web portals, you cannot cache all pages universally. Therefore, you must establish two dynamic bypass rules:
- Session Bypasses: Build rules that instruct the CDN to bypass caching when a request carries authentication or session cookies, so logged-in users always receive personalised responses while anonymous visitors are still served from the edge cache.
- Query String Sorting: Configure the edge database cache to ignore minor analytics variables (like UTM tags) when evaluating cache keys. Consequently, this prevents cache fragmentation.
To deploy an enterprise edge caching strategy safely, work through this technical validation sequence. Adopt these four optimisation steps:
- Audit HTTP Headers: Verify that your origin server sends clean
Cache-ControlandVaryheaders without auth blocks. - Draft Modular Cache Rules: Configure target Cache Rules to store static category directories for up to 30 days.
- Build Authentication Exceptions: Create rules to bypass the edge cache when login cookies are detected.
- Deploy Purge API Webhooks: Configure database save actions to trigger automated Purge API requests when you update pages.
Performance Comparison: Edge Caching vs. Origin Fetch
To illustrate the speed benefits, the following table details actual loading metrics:
| Performance Metric | Origin Server Fetch (No CDN Cache) | Edge Cache Hit (CDN Active) | Expected Speed Improvement |
|---|---|---|---|
| Time to First Byte (TTFB) | 450 - 800 Milliseconds | 15 - 35 Milliseconds | Up to 95% faster initial server response |
| Mobile LCP (Largest Image) | 3.8 Seconds (Poor) | 1.4 Seconds (Good) | Pass Core Web Vitals metrics cleanly |
| Origin CPU Load | High (Every page queries database) | Minimal (Edge handles 90% of hits) | Reduced hosting costs and higher stability |
Prerequisites
Before you touch the dashboard, make sure you have the following in place:
- A domain already proxied through Cloudflare (the orange-cloud DNS setting, not grey/DNS-only).
- Access to your origin configuration — Nginx, Apache, or the application layer — so you can set response headers.
- An API token scoped to Zone → Cache Purge if you intend to automate purges. Create it under My Profile → API Tokens.
- A way to inspect raw HTTP headers:
curlon the command line, or the Network tab in Chrome DevTools. - A staging URL or a low-traffic path you can experiment on before rolling anything out site-wide.
A Free or Pro plan is enough to follow every step below. Cache Rules are available on all plans, though a few cache-key options and Tiered Cache sit behind higher tiers.
Step 1: Send Correct Cache-Control Headers from the Origin
Cloudflare only treats a response as cacheable if your origin does not actively forbid it. The single most common reason HTML never caches is an origin returning a Set-Cookie header or a restrictive Cache-Control on every request.
Set explicit directives at the origin. In Nginx:
1location ~* \.(css|js|woff2|jpg|png|webp|svg)$ {
2 add_header Cache-Control "public, max-age=31536000, immutable";
3}
4
5location / {
6 # HTML: short browser life, long shared (edge) life
7 add_header Cache-Control "public, max-age=0, s-maxage=86400";
8}
The s-maxage directive targets shared caches such as the Cloudflare edge, while max-age=0 keeps the visitor’s browser revalidating so they never see stale HTML after a deploy. The immutable token on fingerprinted assets tells browsers never to revalidate them at all.
For an application-driven response (PHP or Symfony), express the same intent in code:
1$response->setPublic();
2$response->setMaxAge(0); // browser
3$response->setSharedMaxAge(86400); // edge / s-maxage
Authenticated or personalised responses must opt out explicitly, otherwise a broad rule could serve one user’s page to another:
1Cache-Control: private, no-store
Step 2: Make HTML Eligible for Cache with a Cache Rule
By default Cloudflare marks HTML as DYNAMIC and never stores it. To change that, create a rule under Caching → Cache Rules → Create rule.
Write an expression that matches the pages you want cached while excluding anything dynamic:
1(http.host eq "example.com"
2 and not starts_with(http.request.uri.path, "/wp-admin")
3 and not starts_with(http.request.uri.path, "/cart")
4 and not starts_with(http.request.uri.path, "/checkout")
5 and not starts_with(http.request.uri.path, "/my-account"))
Then set the rule actions:
- Cache eligibility: Eligible for cache — the modern equivalent of the old “Cache Everything” behaviour.
- Edge TTL: Use cache-control header if present, so the
s-maxageyou set in Step 1 wins; fall back to a fixed value such as 1 day if the header is absent. - Browser TTL: Respect origin.
Step 3: Add a Cookie Bypass So Logged-in Users Are Never Cached
This is the step most guides skip, and the one that leaks data when they do. Add a second rule, placed above the eligibility rule, that forces a bypass whenever a genuine session cookie is present. Cloudflare evaluates Cache Rules top-down, so an earlier bypass rule always wins for authenticated visitors.
1http.cookie contains "wordpress_logged_in_"
2or http.cookie contains "wp-postpass_"
3or http.cookie contains "woocommerce_items_in_cart"
4or http.cookie contains "comment_author_"
Set the action to Bypass cache. For non-WordPress stacks, swap the cookie names for your framework’s session identifier — PHPSESSID, laravel_session, connect.sid, and so on. Scope this tightly: matching a broad analytics cookie such as _ga here would accidentally bypass the cache for every anonymous visitor too.
Step 4: Normalise the Cache Key
Two URLs that differ only by a tracking parameter should share one cached object. Inside the eligibility rule, open Cache Key → Query String, choose Ignore specific query string parameters, and list the analytics keys:
1utm_source, utm_medium, utm_campaign, utm_term, utm_content, fbclid, gclid
This collapses /pricing?utm_source=newsletter and /pricing?gclid=123 onto a single cached entry, lifting your hit ratio instead of fragmenting it across thousands of near-duplicate keys.
How to Verify a Cache HIT or MISS
Never assume a rule works — measure it. Every response Cloudflare serves carries a cf-cache-status header. Request the same URL twice and watch it change:
1curl -sI https://example.com/ | grep -i cf-cache-status
2# First request: cf-cache-status: MISS
3# Second request: cf-cache-status: HIT
The values you will encounter and what each one means:
cf-cache-status | Meaning | What to do |
|---|---|---|
| HIT | Served straight from the edge | Working as intended |
| MISS | Not cached yet; fetched from origin and now stored | Re-request to confirm it becomes a HIT |
| DYNAMIC | Cloudflare judged it uncacheable | Rule not matching, or origin forbids caching |
| BYPASS | A rule or cookie bypass skipped the cache | Expected on logged-in requests |
| EXPIRED | TTL elapsed, revalidated with origin | Normal; raise Edge TTL if it happens too often |
| REVALIDATED | Stale, confirmed still fresh via ETag | Normal |
If you only ever see DYNAMIC, the eligibility rule is not firing. Confirm the hostname in the expression, then check the origin is not sending Cache-Control: private or a Set-Cookie on the HTML document.
Common Pitfalls and Troubleshooting
- A
Set-Cookieon every response. Cloudflare will not cache a response that sets a cookie. Analytics plugins, CSRF tokens, and A/B testing tools frequently attach one to the HTML document. Move that logic to an asynchronous request, or strip the header on cacheable paths. BYPASSon anonymous visitors. Almost always a cookie-bypass rule that is too broad — a generic cookie such as_gamatching your expression. Restrict the bypass to real session cookies only.- Stale pages after a deploy. Edge TTL is doing its job; you simply forgot to purge. Trigger a targeted purge on publish rather than shortening the TTL to a few seconds.
- Vary headers being ignored. Cloudflare only varies its cache on
Accept-Encoding; it will not keep separate copies for an arbitraryVary: User-AgentorVary: Cookie. Serve device-specific markup through responsive CSS or a Worker instead of relying on Vary. - Development Mode left switched on. It bypasses the cache for three hours and silently makes every response look uncacheable. Confirm it is off before you test.
Production Considerations and Automated Purging
Once a single path behaves correctly, widen the rule to the full site during a low-traffic window and watch Caching → Overview for your hit ratio — a healthy static site sits comfortably above 90%.
Wire your CMS to purge only what changed rather than the whole zone. A targeted purge by URL keeps neighbouring pages warm in the cache:
1curl -X POST \
2 "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/purge_cache" \
3 -H "Authorization: Bearer ${CF_API_TOKEN}" \
4 -H "Content-Type: application/json" \
5 --data '{"files":["https://example.com/pricing"]}'
Call this from your publish hook so an editor’s update goes live within seconds while everything else stays cached. For large catalogues, group related URLs behind a Cache Tag (Enterprise) or purge by prefix, and enable Tiered Cache to lift the global hit ratio by routing misses through a regional parent before they ever reach your origin.
Partner with a Vetted UK Cloudflare Consultancy
Getting these caching decisions right protects your origin servers and speeds up page delivery for every visitor. Mecanik provides professional technical SEO audit services and infrastructure scaling through our website development page. We specialise in Symfony edge integrations, custom Cloudflare Cache Rules, and edge-native deployments. Contact us today to schedule your technical scoping workshop.
Frequently Asked Questions
What is cloudflare cdn caching? Cloudflare cdn caching is the process of storing static copies of your website’s pages, images, and script files on edge servers located around the world. This configuration allows user requests to be served from the nearest physical server, reducing website load times.
How do I cache HTML pages without leaking user data? To cache HTML safely, configure a Cache Rule with a “Bypass Cache” action that triggers when session or admin cookies are present in request headers. This setup ensures logged-in portal users always fetch dynamic content from the origin database.
What is the difference between Edge TTL and Browser TTL? Edge TTL (Time-To-Live) dictates how long the Cloudflare CDN servers store your content before requesting a fresh copy from your origin server. Conversely, Browser TTL determines how long the visitor’s local browser cache keeps the files.
Why does query string caching affect website performance? If query strings (like UTM tracking tags) are not normalised, the CDN treats each variant as a unique URL, generating duplicate requests to your origin server. Configuring cache key normalisation prevents this crawl duplication.
Can edge caching improve my Core Web Vitals scores? Yes, serving your HTML and media assets directly from edge servers minimises Time-to-First-Byte (TTFB) and Largest Contentful Paint (LCP) times. Consequently, this caching strategy directly improves your mobile page speed ranking.
Comments