WordPress Cloudflare Caching: A Login-Safe Strategy That Wor... cover
· 43 ·

WordPress Cloudflare Caching: A Login-Safe Strategy That Works

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
This post has been read 43 times
Johnny avatar
GitHub Contributions 2166 contributions in the last year
Less More

Leave a Reply