all posts
2025-12-18·5 min read

Cloudflare: Workers Cache Segmentation

CachingCloudflareWordPressWorker

1. Overview & Objective

Following POC provides a way to implement a caching strategy that serves custom content to Bots (e.g., Googlebot) while serving default content to Humans, without sacrificing performance. This kind of approach can be desirable, especially to news sites or sites with paywalled content, where they want bots to have access to the content for SEO reasons, but the paywall to still in place for logged-out users

Goal:

  • Bots: Serve a specific “Bot Version” of the page (cached).
  • Humans: Serve the standard “Default Version” of the page (cached).
  • Performance: Ensure the Cache HIT for both segments.

2. The Challenge: Why PHP Alone Wasn’t Enough

WP Engine uses Varnish as a caching layer that sits in front of the PHP application.

  1. The “Chicken and Egg” Problem: By the time a request reaches PHP to check User-Agent, Varnish has already processed the request.
  2. Cache Collision: If PHP detects a bot and serves “Bot Content,” Varnish might unknowingly cache that content under the “Default” bucket. This leads to Cache Poisoning, where human users accidentally see the Bot version.
  3. No Varnish Access: Since WP Engine is Managed Host you cannot easily modify Varnish VCL, and making changes in Nginx is insufficient because Varnish sits on top of it.

3. The Solution: Cloudflare “Edge Injection”

We utilise Cloudflare Workers to intercept the request at the edge (before it reaches WP Engine).

Architecture Flow

  1. Request: User/Bot visits the site.
  2. Cloudflare Worker: Checks the User-Agent.
    • If Bot: Injects the wpe-us=bot cookie into the request headers transparently.
    • If Human: Passes the request through untouched.
  3. WP Engine Varnish: Receives the request.
    • If Bot: Sees the injected cookie, translates it to X-WPENGINE-SEGMENT: bot, and serves from the Bot Cache Bucket.
    • If Human: Sees no cookie, serves from the Default Cache Bucket.
  4. PHP Application: Receives the request (if uncached) with the correct X-WPENGINE-SEGMENT header already set.

Result: Zero PHP overhead for detection, cache compatibility, and zero risk of cache poisoning.


4. Implementation Details

A. Cloudflare Worker Script

Create a new Worker in Cloudflare and map it to example.com/*.

Worker Code:

export default {
  async fetch(request, env, ctx) {
    const ua = request.headers.get('User-Agent') || '';

    // Detect Googlebot or our Custom Test Agent
    // Case-insensitive regex match
    // Customer able to modify the list to their desire list of bot User Agents
    if (/Googlebot|Caching-Bot/i.test(ua)) {
      // Clone the request to modify headers
      const newRequest = new Request(request);
      const currentCookies = newRequest.headers.get('Cookie') || '';

      // Inject the 'wpe-us=bot' cookie
      // If cookies exist, append it. If not, create it.
      const newCookies = currentCookies ? `${currentCookies}; wpe-us=bot` : 'wpe-us=bot';
      newRequest.headers.set('Cookie', newCookies);

      // Forward modified request to Origin (WP Engine)
      return fetch(newRequest);
    }

    // Pass human traffic through untouched
    return fetch(request);
  }
};

B. WordPress Shortcode (Testing Logic)

Add this to functions.php to create the [segmentation_test] shortcode. This PHP handles the Content Logic (what to show) and ensures the Vary header is set.

PHP Code:

function wpe_segmentation_final_style_shortcode() {
    // 1. CRITICAL: Tell Varnish to separate cache based on the segment header
    header("Vary: X-WPENGINE-SEGMENT");

    // 2. Get Data
    $ua = $_SERVER['HTTP_USER_AGENT'];
    // We rely on the Header because Varnish consumes the Cookie to create it
    $segment_header = isset($_SERVER['HTTP_X_WPENGINE_SEGMENT']) ? $_SERVER['HTTP_X_WPENGINE_SEGMENT'] : '';

    // 3. Display Output
    ob_start();
    ?>
    <div style="border: 2px solid #333; padding: 0; background: #fff; font-family: sans-serif; max-width: 600px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); margin-top: 20px;">
        <div style="background: #333; color: #fff; padding: 20px; border-bottom: 2px solid #000;">
            <h3 style="margin:0 0 10px 0; font-size: 20px; border-bottom: 1px solid #555; padding-bottom: 10px;">Bot vs. Human Check</h3>
            <div style="font-size: 12px; color: #ccc; margin-bottom: 4px; font-weight: bold; text-transform: uppercase;">User Agent Detected:</div>
            <div style="font-family: monospace; font-size: 11px; color: #fff; background: #222; padding: 8px; border-radius: 4px; border: 1px solid #444; word-break: break-all; line-height: 1.4;">
                <?php echo esc_html($ua); ?>
            </div>
        </div>
        <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 20px; text-align: center; border-bottom: 1px solid #eee;">
            <div>
                <div style="font-size: 11px; text-transform: uppercase; color: #888; font-weight: bold; margin-bottom: 5px;">Server Segment</div>
                <code style="background: #f4f4f4; padding: 6px 4px; border-radius: 4px; font-size: 14px; display: block; font-weight: bold;">
                    <?php echo $segment_header ? esc_html($segment_header) : '<span style="color:#bbb; font-weight:normal;">Default</span>'; ?>
                </code>
            </div>
            <div>
                <div style="font-size: 11px; text-transform: uppercase; color: #888; font-weight: bold; margin-bottom: 5px;">Cache Status</div>
                <div id="x-cache-display" style="font-size: 14px; font-weight: bold; color: gray; padding-top: 3px;">Checking...</div>
            </div>
        </div>
        <div style="padding: 30px; color: white; text-align: center; font-weight: bold; font-size: 24px; <?php echo ($segment_header === 'bot') ? 'background: #d63384;' : 'background: #198754;'; ?>">
            <?php if ($segment_header === 'bot') { echo "🤖 BOT VERSION"; } else { echo "🌍 HUMAN / DEFAULT VERSION"; } ?>
        </div>
    </div>
    <script>
    (function() {
        // Fetch headers to display X-Cache status
        fetch(window.location.href, { method: 'HEAD' })
            .then(response => {
                const cacheStatus = response.headers.get('x-cache');
                const display = document.getElementById('x-cache-display');
                if (cacheStatus) {
                    const color = cacheStatus.includes('HIT') ? 'green' : '#d9534f';
                    display.innerHTML = `<span style="color:${color}">${cacheStatus}</span>`;
                } else {
                    display.innerText = "N/A";
                }
            });
    })();
    </script>
    <?php
    return ob_get_clean();
}
add_shortcode('segmentation_test', 'wpe_segmentation_final_style_shortcode');

5. Verification & Testing

How to Verify (Browser)

  1. Open Chrome DevTools → cmd+shift+P → Network Conditions.
  2. Uncheck “Select automatically” next to User agent.
  3. Test Bot: Select “Custom” and paste Caching-Bot.
    • Result: Pink Banner (“BOT VERSION”).
    • Cache: HIT.
  1. Test Human: Uncheck “Custom” (Default UA).
    • Result: Green Banner (“HUMAN VERSION”).
    • Cache: HIT.

How to Verify (Curl)

Run these commands in a terminal to verify without browser cache interference.

  1. Bot Test (Expect “BOT VERSION”):

curl -A "Caching-Bot" https://your-site.com/test-page/ | grep "BOT VERSION"

  1. Human Test (Expect “DEFAULT VERSION”):

curl -A "Mozilla/5.0" https://your-site.com/test-page/ | grep "DEFAULT VERSION"

FAQ: Why is the Cookie “None” in PHP?

You may notice that when testing the Bot, PHP reports the wpe-us cookie as None, even though the Bot logic is working.

  • Reason: The cookie is injected by Cloudflare at the edge. When it hits WP Engine, Varnish consumes (removes) the cookie to generate the X-WPENGINE-SEGMENT header.
  • Conclusion: This is expected behaviour. The Header is the source of truth for the application.

Found this useful?

There's more where that came from — explore the rest of the documentation, experiments, and production work.

Back to the index