Key Takeaways
Running page caching in both WP Rocket and Cloudflare causes double caching, stale content, and purge conflicts
Minification should happen in one place only. Running it in both creates CSS and JavaScript rendering issues
Cloudflare APO is the single biggest performance upgrade for most WordPress sites at $5/month
WP Rocket handles WordPress-specific caching. Cloudflare handles edge delivery. Let each tool do what it does best
A correct setup can drop your global TTFB from 800ms to under 100ms for cached pages
The problem nobody tells you about
WP Rocket and Cloudflare are two of the most popular performance tools in the WordPress world. WP Rocket handles page caching, file optimization, and lazy loading. Cloudflare handles CDN, DDoS protection, and edge caching. They work well together.
When configured correctly.
The problem is that most sites have them both running with default or near-default settings, and those defaults overlap in ways that cause real issues. Both tools try to cache pages. Both tools try to minify CSS and JavaScript. Both tools try to handle browser caching headers. When two systems try to do the same job, you get conflicts.
I've audited over 50 WordPress sites running this combination. Nearly all of them had at least one of these wrong:
- Page caching running in both WP Rocket and Cloudflare (double caching)
- Minification enabled in both (CSS/JS rendering issues)
- No cache purge connection between them (stale content after updates)
- Cloudflare APO not configured, or conflicting with WP Rocket's page cache
Here's the exact configuration I use across all client sites.
Step 1: connect WP Rocket to Cloudflare
Before touching any cache settings, connect the two systems so they can communicate. This is the most important step and the one most people skip.
In your WordPress admin, go to WP Rocket > Cloudflare (or WP Rocket > Add-ons on newer versions).
You need two things:
- Cloudflare account email: the email you log into Cloudflare with
- Global API Key: find this in your Cloudflare dashboard under My Profile > API Tokens > Global API Key
Enter both and save. WP Rocket will verify the connection.
Why this matters: when WP Rocket purges its cache (after a post update, a theme change, or a manual purge), it will now also purge the corresponding pages from Cloudflare's cache. Without this connection, you clear WP Rocket's cache but Cloudflare keeps serving the old version to visitors. That's why people update a page and nothing changes for their visitors.
A note on API tokens vs Global API Key: Cloudflare also offers scoped API tokens with limited permissions. WP Rocket's Cloudflare integration requires the Global API Key, not a scoped token. This is a limitation of the WP Rocket integration, not a Cloudflare issue.
Step 2: WP Rocket settings (the WordPress side)
Caching
Go to WP Rocket > Cache:
- Enable caching for mobile devices: ON. WP Rocket serves the correct cached version based on device type.
- Separate cache files for mobile devices: ON if your theme serves different HTML for mobile vs desktop. OFF if your site is responsive (same HTML, different CSS). Most modern themes are responsive, so this is usually OFF.
- Cache lifespan: 10 hours is a good default. Shorter lifespan means more frequent regeneration (more server load). Longer means higher chance of stale content. 10 hours balances both.
File Optimization
Go to WP Rocket > File Optimization:
- Minify CSS: ON
- Combine CSS: OFF. Combining CSS files breaks HTTP/2 multiplexing and can cause render-blocking issues. Modern browsers handle multiple small files better than one large file.
- Optimize CSS delivery: ON. This generates critical CSS and defers the rest.
- Minify JavaScript: ON
- Combine JavaScript: OFF. Same reason as CSS.
- Defer JavaScript: ON, with the "Safe Mode" initially enabled. Test your site after enabling this. If things break (sliders not loading, menus not working), you need to exclude specific scripts.
Important: minification should only happen in one place. WP Rocket handles it here. We will turn it OFF in Cloudflare.
Media
Go to WP Rocket > Media:
- LazyLoad images: ON
- LazyLoad iframes/videos: ON
- Add missing image dimensions: ON (prevents Cumulative Layout Shift)
Preloading
Go to WP Rocket > Preload:
- Activate Preloading: ON. WP Rocket will crawl your sitemap and pre-generate cached pages.
- Sitemap-based cache preloading: ON. Point it to your XML sitemap URL.
- Preload links: ON. When a visitor hovers over a link, WP Rocket starts loading the target page in the background. This makes navigation feel instant.
Advanced Rules
Go to WP Rocket > Advanced Rules:
- Never cache URLs: Make sure WooCommerce dynamic pages are listed here if they're not auto-detected:
/cart//checkout//my-account/- Any other logged-in or user-specific pages
- Never cache cookies: WP Rocket handles WooCommerce cookies by default, but verify
woocommerce_items_in_cartandwoocommerce_cart_hashare in the list.
Database
Go to WP Rocket > Database:
- Expired transients: schedule automatic cleanup. (This overlaps with what I covered in the wp_options cleanup guide, but WP Rocket's version is a lighter touch for ongoing maintenance.)
Step 3: Cloudflare settings (the CDN side)
Log into your Cloudflare dashboard and select your domain.
SSL/TLS
- SSL mode: Full (Strict). This encrypts traffic between visitors and Cloudflare, and between Cloudflare and your server. Your server needs a valid SSL certificate (most hosts provide this through Let's Encrypt).
- Always Use HTTPS: ON
- Automatic HTTPS Rewrites: ON
Speed > Optimization
Here's where most configurations go wrong:
- Auto Minify: OFF for CSS, JavaScript, and HTML. WP Rocket already handles minification. Running it twice can break things. Cloudflare's minification is also less WordPress-aware than WP Rocket's.
- Brotli: ON. This is compression at the edge and doesn't conflict with anything. Brotli compresses 15-20% better than gzip.
- Early Hints: ON. Cloudflare sends
103 Early Hintsheaders to start loading critical assets before the HTML even arrives. This works well alongside WP Rocket's preloading. - Rocket Loader: OFF. This is Cloudflare's JavaScript deferral system. WP Rocket already handles JS deferral. Rocket Loader conflicts with WP Rocket's defer settings and frequently breaks WordPress sites. I've seen it cause blank pages, broken forms, and non-functional navigation menus. Turn it off.
Caching > Configuration
- Caching Level: Standard
- Browser Cache TTL: Respect Existing Headers. Let WP Rocket control browser cache headers. If you set a Cloudflare override, you lose WP Rocket's per-file-type control.
Caching > Tiered Cache
- Tiered Cache Topology: ON (Smart). This routes cache misses through Cloudflare's upper-tier data centers before hitting your origin server. Reduces load on your server and improves cache hit ratios.
Step 4: Cloudflare Page Rules (if not using APO)
If you're not using Cloudflare APO (covered in the next section), you need page rules to prevent Cloudflare from interfering with WordPress dynamic pages.
You get 3 free page rules on Cloudflare's free plan. Use them:
Rule 1: Bypass cache for wp-admin
- URL:
*yourdomain.com/wp-admin* - Setting: Cache Level > Bypass
Rule 2: Bypass cache for wp-login
- URL:
*yourdomain.com/wp-login.php* - Setting: Cache Level > Bypass
Rule 3: Bypass cache for WooCommerce pages (if applicable)
- URL:
*yourdomain.com/cart*and*yourdomain.com/checkout*and*yourdomain.com/my-account* - Setting: Cache Level > Bypass
- Note: you may need to split this into separate rules or use a wildcard pattern
Do NOT create a page rule that says "Cache Everything" for your entire domain. This is the single most common mistake I see. It forces Cloudflare to cache your HTML pages, including wp-admin, cart, checkout, and logged-in user pages. If a customer adds an item to their cart and the next visitor sees someone else's cart contents, that's what happened.
Step 5: Cloudflare APO (the real performance unlock)
Cloudflare APO (Automatic Platform Optimization) for WordPress costs $5/month and it's the single biggest performance upgrade most WordPress sites can make.
What APO does
Without APO, Cloudflare only caches static assets: images, CSS, JavaScript, fonts. Your HTML pages are fetched from your origin server on every request.
With APO, Cloudflare caches the full HTML page at the edge. A visitor in Singapore hitting a server in Amsterdam doesn't wait for the round trip. Cloudflare serves the complete page from a Singapore data center.
The TTFB difference is dramatic:
| Scenario | TTFB (Singapore visitor, Amsterdam server) |
|---|---|
| No Cloudflare | 600-1200ms |
| Cloudflare free (static assets only) | 600-1200ms (HTML still from origin) |
| Cloudflare + APO | 30-80ms (HTML from edge) |
How to enable APO
- In Cloudflare dashboard: go to Speed > Optimization > Cloudflare APO and enable it ($5/month on the free plan, included on Pro+ plans)
- In WordPress: install and activate the Cloudflare plugin (the official one, not a third-party plugin)
- In the Cloudflare plugin settings: enter your API credentials and enable APO
APO + WP Rocket: how they work together
This is the part that confuses people. With APO enabled, you have two layers of page caching:
- WP Rocket: generates the cached HTML on your server
- Cloudflare APO: copies that cached HTML to edge data centers worldwide
The flow works like this:
- First visitor requests a page
- WP Rocket serves its cached version from your server
- Cloudflare APO stores that response at the edge
- All subsequent visitors get the page from Cloudflare's edge (your server is never hit)
- When you update the page, WP Rocket purges its cache AND tells Cloudflare to purge (because you connected them in Step 1)
- The next visitor triggers a fresh copy from your server, which Cloudflare caches again
This is not "double caching" in the conflicting sense. WP Rocket generates the optimized page. Cloudflare distributes it globally. Each tool does its own job.
APO bypasses for dynamic content
APO is smart enough to skip caching for:
- Logged-in WordPress users
- Pages with WooCommerce cookies (cart, checkout, my-account)
- Any page where WP Rocket sends no-cache headers
You don't need manual page rules for these when APO is active. APO reads the cache-control headers that WP Rocket sets and respects them.
If you enabled APO, remove any "Cache Everything" page rules. APO replaces them. Having both active causes conflicts.
Step 6: verify the configuration
After setting everything up, run these checks:
Check 1: response headers
Open your browser's developer tools (F12), go to the Network tab, and load your homepage. Click on the main document request and check the response headers:
cf-cache-status: HIT: Cloudflare served the page from cache. This is what you want for static pages.cf-cache-status: DYNAMIC: Cloudflare fetched the page from your server. Expected for logged-in pages, cart, checkout.cf-cache-status: BYPASS: Cloudflare skipped caching entirely. Expected for wp-admin.cf-apo-via: tcache: APO served this page from edge cache.
Load the same page a second time. If the first load says MISS and the second says HIT, caching is working. The first request populated the cache, the second was served from it.
Check 2: test with different locations
Use a tool like KeyCDN Performance Test or curl from different regions to check TTFB from various locations. With APO active, you should see sub-100ms TTFB from most locations worldwide.
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\n" https://yourdomain.comCheck 3: test WooCommerce dynamic pages
If you're running WooCommerce:
- Add a product to the cart
- Go to the cart page
- Check that the response headers show
cf-cache-status: DYNAMICorBYPASS(notHIT) - Open an incognito window and visit the cart page. It should be empty (not showing the other session's cart)
If the incognito cart shows items from your other session, caching is leaking dynamic content. Review your page rules and APO bypass settings immediately.
Check 4: test cache purging
- Edit a page or post in WordPress
- Click Update
- Visit the page in an incognito window
- Verify you see the updated content
If the old content still shows, the purge connection between WP Rocket and Cloudflare isn't working. Go back to Step 1 and verify the API key connection.
Common mistakes and how to fix them
Cache stampede after purging
When you purge the cache (manually or after an update), every next visitor request hits your origin server because there's no cached version. If 100 visitors hit the site in the next 10 seconds, your server handles 100 uncached requests simultaneously. On shared hosting, this can crash your site.
Fix: WP Rocket's preloading feature helps here. After a purge, WP Rocket automatically crawls your sitemap and regenerates cached pages. Make sure preloading is enabled (Step 2). On high-traffic sites, consider using Cloudflare's "Always Online" feature as a safety net during cache regeneration.
Mixed content warnings after enabling Full (Strict) SSL
If your site loads some resources over HTTP instead of HTTPS, browsers block them. This usually means hardcoded http:// URLs in your database (from before you had SSL).
Fix: enable "Automatic HTTPS Rewrites" in Cloudflare (covered in Step 3). For persistent issues, run a search-and-replace on your database to change http://yourdomain.com to https://yourdomain.com. The WP-CLI way:
wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' --skip-columns=guidJavaScript breaking after enabling defer
Some WordPress plugins and themes assume their JavaScript runs immediately when the page loads. Deferring it changes the execution order, which can break sliders, popup forms, analytics tracking, and menu toggles.
Fix: in WP Rocket's File Optimization settings, add the breaking scripts to the "Exclude from Defer" list. Common scripts to exclude:
- jQuery (
/wp-includes/js/jquery/jquery.min.js) - Slider plugins (look for the script URL in your page source)
- Form plugins (Gravity Forms, WPForms)
Test after each exclusion to confirm the issue is resolved.
The full settings checklist
For quick reference, here's the complete configuration in one place:
WP Rocket
| Setting | Value |
|---|---|
| Mobile caching | ON |
| Cache lifespan | 10 hours |
| Minify CSS | ON |
| Combine CSS | OFF |
| Optimize CSS delivery | ON |
| Minify JS | ON |
| Combine JS | OFF |
| Defer JS | ON |
| LazyLoad images | ON |
| Preloading | ON |
| Cloudflare API connected | YES |
Cloudflare
| Setting | Value |
|---|---|
| SSL mode | Full (Strict) |
| Auto Minify | OFF (all three) |
| Brotli | ON |
| Rocket Loader | OFF |
| Early Hints | ON |
| Browser Cache TTL | Respect Existing Headers |
| Tiered Cache | Smart |
| APO | ON ($5/month) |
Related reading
- How to Clean Your wp_options Table (The Right Way). Database optimization that complements caching. No cache can save a page that takes 3 seconds to generate.
- WordPress Slow After 2 Years? Here's What's Actually Wrong. The 5 most common causes of WordPress slowdown, with diagnostic SQL for each.
- WordPress under 2 seconds: the full speed checklist. The complete optimization playbook from server to browser.
- Cheap website hosting costs more than you think. When your origin server is the bottleneck, caching masks the problem but doesn't fix it.

Written by
Barry van Biljon
Full-stack developer specializing in high-performance web applications with React, Next.js, and WordPress.
Ready to Get Started?
Have questions about implementing these strategies? Our team is here to help you build high-performance web applications that drive results.
