seoedgeai.com Blog

Edge SEO Optimization Techniques for Cloudflare Workers

Five practical Cloudflare Worker techniques for edge SEO: conditional title injection by user-agent, A/B testing titles at the edge, dynamic JSON-LD generation, crawler header control, and performance tuning.

Close-up of rack-mounted server hardware in a data center with green LED status lights visible on drive bays
Brett Sayles, Pexels License, via Pexels

Edge SEO means rewriting what search engines see at the CDN edge, between the crawler and your origin server. For a site on Cloudflare, the tool for that job is a Cloudflare Worker. This article covers five specific Worker techniques for edge SEO optimization: conditional title injection by user-agent, A/B testing titles without a separate tool, dynamic JSON-LD per page, HTTP header control for crawlers, and keeping Worker CPU time under 10 ms.

If you need the foundations first (what edge SEO is, what HTMLRewriter does, how Workers sit in front of your origin), read What Edge SEO Is and Why Cloudflare Users Need It and the Edge SEO Strategy Guide. This article assumes you are already comfortable with wrangler deploy and want the specific techniques that move search metrics.

Conditional title injection based on user-agent

Googlebot and Bingbot see the same <title> as a human visitor by default. That is a missed opportunity. A product category page might rank better with a crawler-targeted title that matches a search pattern (“Buy Wireless Headphones 2026”) while showing humans a brand-oriented title (“AudioCo Wireless Collection”).

The Worker reads the User-Agent header and picks a title from a lookup before the response streams out.

const TITLES = {
  default: "AudioCo Wireless Collection",
  googlebot: "Buy Wireless Headphones 2026 | AudioCo",
  bingbot: "Wireless Headphones 2026 - AudioCo",
};

export default {
  async fetch(request, env, ctx) {
    const ua = (request.headers.get("User-Agent") || "").toLowerCase();
    const isBot = ua.includes("googlebot") || ua.includes("bingbot");
    const title = isBot
      ? TITLES[ua.includes("bingbot") ? "bingbot" : "googlebot"]
      : TITLES.default;

    const response = await fetch(request);
    return new HTMLRewriter()
      .on("title", {
        element(el) {
          el.setInnerContent(title);
        },
      })
      .transform(response);
  },
};

Because HTMLRewriter operates on the streaming response, the origin still serves its normal HTML. The Worker swaps the title as bytes pass through Cloudflare’s network, typically in under 2 ms of CPU time. You can store the title map in Workers KV if per-URL or per-page titles are needed, using cacheTtl: 300 to avoid reading KV on every request.

https://images.pexels.com/photos/5483071/pexels-photo-5483071.jpeg?cs=srgb&dl=pexels-cottonbro-5483071.jpg&fm=jpg

Hands typing on a silver laptop keyboard with code editor visible on screen in dim blue light
cottonbro, Pexels License, via Pexels

A/B testing titles at the edge

Running a title test normally involves a CMS plugin, a JavaScript snippet, and waiting for Google to re-crawl. With Workers you can split traffic at the edge using Math.random().

const TITLE_VARIANTS = {
  A: "Organic Dog Food | Healthy Paws",
  B: "Best Organic Dog Food 2026 | Healthy Paws",
};

export default {
  async fetch(request, env, ctx) {
    const variant = Math.random() < 0.5 ? "A" : "B";
    const response = await fetch(request);

    return new HTMLRewriter()
      .on("title", {
        element(el) {
          el.setInnerContent(TITLE_VARIANTS[variant]);
        },
      })
      .transform(response);
  },
};

The variant decision happens once per request with no external state. Log the variant to a KV namespace or R2 on a sampling basis so you can measure click-through rate per variant in Search Console after Google has crawled both versions. Run each test for at least two weeks to reach statistical significance given crawl frequency.

For more control, use request.cf.region or request.cf.country to segment by geography, or store a test assignment in a cookie so the same visitor always sees the same variant across pages. The cost is zero additional infrastructure: the Worker is your test framework.

Dynamic JSON-LD generation

Structured data is where edge SEO delivers the highest return per byte rewritten. A Worker can inject JSON-LD that is specific to the URL being requested, drawing on a lookup table or an external API, without touching your CMS templates.

const SCHEMA_MAP = {
  "/products/wireless-headphones": {
    "@context": "https://schema.org",
    "@type": "Product",
    name: "Wireless Headphones Pro",
    offers: { "@type": "Offer", price: "149.00", priceCurrency: "USD" },
  },
  "/about": {
    "@context": "https://schema.org",
    "@type": "Organization",
    name: "AudioCo",
    description: "Premium audio equipment manufacturer",
  },
};

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const schema = SCHEMA_MAP[url.pathname] || null;
    if (!schema) {
      return await fetch(request);
    }

    const response = await fetch(request);
    return new HTMLRewriter()
      .on("head", {
        element(el) {
          el.append(
            `<script type="application/ld+json">${JSON.stringify(schema)}</script>`,
            { html: true }
          );
        },
      })
      .transform(response);
  },
};

You can scale this by storing schema fragments in KV and fetching only the fragment for the requested path. For sites with thousands of URLs, batch-load the schema for likely paths using env.KV_NAMESPACE.get(keys) which returns a Map and accepts up to 100 keys per call. The Workers KV read documentation confirms the 100-key limit and the optional cacheTtl parameter that keeps results warm in the colocated cache for as little as 30 seconds.

Header manipulation for crawlers

HTTP headers are the fastest thing a Worker can change because they are written before the body starts streaming. Two patterns matter for edge SEO:

X-Robots-Tag per path. You can block thin content from indexing without touching the CMS.

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const response = await fetch(request);

    if (url.pathname.startsWith("/tag/") || url.pathname.startsWith("/author/")) {
      const newHeaders = new Headers(response.headers);
      newHeaders.set("X-Robots-Tag", "noindex");
      return new Response(response.body, {
        status: response.status,
        statusText: response.statusText,
        headers: newHeaders,
      });
    }
    return response;
  },
};

Link header for pagination. Instead of editing templates to add rel="next" and rel="prev" in the HTML, inject them as HTTP headers where Google and Bing both read them.

if (url.pathname.match(/\/page\/\d+/)) {
  const newHeaders = new Headers(response.headers);
  const baseUrl = `${url.origin}${url.pathname.replace(/\/page\/\d+/, "")}`;
  newHeaders.set("Link", `<${baseUrl}>; rel="canonical"`);
  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers: newHeaders,
  });
}

Header modifications cost zero CPU time beyond constructing the new Response wrapper. They are the cheapest edge SEO intervention you can make.

Performance tuning of Worker scripts

Edge SEO changes are worthless if the Worker adds latency. Cloudflare Workers have a 10 ms CPU limit on the Free plan and 128 MB of memory. The average Worker uses about 2.2 ms of CPU per request. Here are the specific practices that keep your SEO Worker inside that budget:

Use HTMLRewriter, not full-body regex. HTMLRewriter parses the response as a stream and only buffers the elements you select. A regex on the full HTML body forces the entire page into memory and then scans it, typically using 5-10x more CPU. The Workers documentation describes HTMLRewriter as a “jQuery-like experience” that operates on the streaming output.

Minimize KV reads. Each get() adds 5-15 ms of wall time (not CPU, but wall time can trigger timeout cascades). Use cacheTtl so that repeated requests for the same URL hit the colocated cache instead of the KV backing store. Batch multi-key lookups: one get(keys) call replaces five individual get(key) calls.

Fail open. Wrap your rewrite logic so that if the Worker throws, the origin response reaches the visitor unmodified.

export default {
  async fetch(request, env, ctx) {
    try {
      const response = await fetch(request);
      return new HTMLRewriter()
        .on("title", { element(el) { el.setInnerContent("Fallback Title"); } })
        .transform(response);
    } catch {
      return await fetch(request);
    }
  },
};

Use the Cache API for expensive lookups. If you build JSON-LD from a database or API, cache the result at the edge with caches.default.put() so that subsequent requests for the same URL serve from cache. The Cache API works per data center, so a popular URL gets one upstream call per colo instead of one per request.

Profile with wrangler tail. Deploy your Worker with wrangler tail running. It shows CPU time and wall time per invocation. If any request exceeds 5 ms of CPU, examine that path for expensive operations.

SEOEdgeAI’s proxy implements all of these patterns: it uses a Worker to route traffic through a rewrite proxy, fails open if the proxy errors, and stores per-URL title and schema data in KV with caching. You can see the architecture described step by step on the how it works page.

Chris Lever’s talk at brightonSEO April 2025 walks through real Worker-based SEO optimizations from the perspective of a technical SEO practitioner. The section starting around 6:30 covers header manipulation and title rewriting patterns that mirror the techniques above.

What edge SEO changes for your workflow

The techniques in this article each solve a specific problem: crawling signals that are wrong per path, titles that do not match search intent, structured data that is missing or generic, and tests that require a developer every time. A Cloudflare Worker running these patterns costs nothing beyond the Workers plan (Free tier includes 100,000 requests per day) and goes live seconds after wrangler deploy.

None of them replace a CMS for content creation or a backend for dynamic data. They solve a different problem: changing what search engines see without waiting for a deployment cycle. For sites already on Cloudflare, that speed advantage is the core of edge SEO optimization.

If managing these Workers yourself sounds like overhead, SEOEdgeAI’s SEO product runs the same patterns as a managed service: it injects titles, meta descriptions, JSON-LD, and headers through its own edge proxy, reads Search Console data to decide what to change, and publishes blog content from its engine. It connects in about two seconds with no code changes.

Companies building their own edge SEO stack often track competitor hiring to understand what techniques are entering the mainstream. InsightMoves monitors competitor careers pages for SEO and engineering roles, giving you a signal on where competitor teams are investing next.

Published by seoedgeai.com.

Visit seoedgeai.com

Made with AI.