Revision summary This article was updated to document a real-world production issue where Google Search visitors intermittently received a broken layout due to Cloudflare caching a cold-start HTML variant. The root cause and the final corrected strategy are recorded in Version 2026-01-20.
WordPress Cloudflare Caching: A Login-Safe Strategy That Works
This article documents a production-ready WordPress Cloudflare caching strategy that safely accelerates frontend pages without breaking logged-in sessions.
Observed issue Logged-in users received cached HTML on the frontend even when WordPress returned private and no-store headers.
Core decision
Cloudflare should never guess WordPress login state. The application must expose a single, explicit cache signal.
Design rule Use one explicit signal only. Avoid WordPress internal cookies and heuristics.
WordPress implementation
Logged-in users receive a dedicated cookie used only for cache decisions. Anonymous visitors never receive it.
<?php
add_action('init', function () {
if (is_user_logged_in()) {
setcookie(
'cf_logged_in',
'1',
time() + 3600,
'/',
'',
is_ssl(),
true
);
}
});
Cloudflare cache rules
Create rules in the exact order shown. APO disabled. No Workers. No Page Rules.
Rule 1 highest priority
http.request.method eq "GET"
and http.cookie contains "cf_logged_in=1"
Action
Cache: Bypass
Rule 2
http.request.method eq "GET"
and (
http.request.uri.path eq "/sitemap.xml"
or http.request.uri.path eq "/sitemap_index.xml"
or http.request.uri.path wildcard "/sitemap*.xml"
or http.request.uri.path wildcard "/wp-sitemap*.xml"
)
Action
Cache: Cache everything
Edge TTL: 12h
Browser TTL: Respect origin
Query string: Ignore
Rule 3
http.request.method eq "GET"
and not http.cookie contains "cf_logged_in=1"
and http.request.uri.query eq ""
and not starts_with(http.request.uri.path, "/wp-admin")
and not http.request.uri.path eq "/wp-login.php"
and not http.request.uri.path eq "/xmlrpc.php"
and not starts_with(http.request.uri.path, "/wp-json/")
and not http.request.uri.path wildcard "/feed*"
Action
Cache: Cache everything
Edge TTL: 1h
Browser TTL: Respect origin
Rule 4
http.request.uri.path wildcard "*.css"
or http.request.uri.path wildcard "*.js"
or http.request.uri.path wildcard "*.jpg"
or http.request.uri.path wildcard "*.jpeg"
or http.request.uri.path wildcard "*.png"
or http.request.uri.path wildcard "*.gif"
or http.request.uri.path wildcard "*.svg"
or http.request.uri.path wildcard "*.webp"
or http.request.uri.path wildcard "*.avif"
or http.request.uri.path wildcard "*.woff"
or http.request.uri.path wildcard "*.woff2"
or http.request.uri.path wildcard "*.ico"
Action
Cache: Cache everything
Edge TTL: 30d
Browser TTL: Respect origin
Required operation
Cloudflare → Caching → Purge → Purge Everything
Logged-in users always bypass cache. Anonymous visitors receive cached HTML. Static assets and sitemaps remain cached.
Version revision · 2026-01-20
This revision documents a production incident where Google Search visitors (incognito / first-visit) intermittently saw a full-width, broken layout. The final fix is recorded below.
Issue From Google Search in incognito mode, the site sometimes rendered with an incorrect full-width layout, while direct visits appeared normal.
Root cause Cloudflare cached an unstable anonymous cold-start HTML variant (first-visit / no cookie state) under Cache Everything, and Google Search traffic repeatedly hit that cached variant.
Final fix Introduce an explicit visitor-level cookie and update cache rules so that cold-start HTML is never cached. Only stabilized anonymous responses are eligible for Cache Everything.
Modify wordpress theme function
Add a new visitor-level cookie (cf_visitor) in the WordPress theme functions.php file to explicitly distinguish first-time requests from stabilized anonymous traffic and prevent Cloudflare from caching cold-start HTML.
<?php
add_action('init', function () {
if (!isset($_COOKIE['cf_visitor'])) {
setcookie(
'cf_visitor',
'1',
time() + 3600,
'/',
'',
is_ssl(),
true
);
}
if (is_user_logged_in()) {
setcookie(
'cf_logged_in',
'1',
time() + 3600,
'/',
'',
is_ssl(),
true
);
}
});
Rule 3 (final)
http.request.method eq "GET"
and not http.cookie contains "cf_logged_in=1"
and http.request.uri.query eq ""
and not starts_with(http.request.uri.path, "/wp-admin")
and not http.request.uri.path eq "/[your custom login path]"
and not http.request.uri.path eq "/wp-login.php"
and not http.request.uri.path eq "/xmlrpc.php"
and not starts_with(http.request.uri.path, "/wp-json/")
and not http.request.uri.path wildcard "/feed*"
Action
Cache: Cache everything
Edge TTL: 1h
Browser TTL: Respect origin
Result Google Search first-visit traffic no longer receives the broken cached layout. Logged-in sessions remain protected via Rule 1 (Bypass).
Operational note
Cloudflare → Caching → Purge → Purge Everything
Leave a Reply