Feature Flags

Edge Experimentation: Running A/B Tests at the CDN Layer

Edge A/B testing explained: how Cloudflare Workers, Vercel Edge Middleware, Lambda@Edge, and Fastly Compute avoid flicker, and when it is overkill.

Abstract editorial illustration of a glowing global network of interconnected nodes radiating outward in dark green and teal tones, representing distributed edge computing infrastructure

Edge A/B testing means deciding which variation a visitor sees at a CDN point of presence, physically close to that visitor, instead of in the browser or on a single distant origin server. It borrows the biggest advantage of server-side testing, no flicker, and adds a second one on top: the decision happens at one of hundreds of network locations near the visitor rather than a single region, which cuts the round trip that traditional server-side rendering can add. This is not a replacement for the feature flag concepts that already run most server-side experiments, it is those same concepts executed on different infrastructure, with a different set of tradeoffs this guide covers in full: how it actually removes flicker, what native geo-targeting looks like in practice, a neutral look at Cloudflare Workers, Vercel Edge Middleware, AWS Lambda@Edge, and Fastly Compute, and the risks nobody mentions until a cache serves the wrong visitor the wrong variant.

Three places a variation decision can happen

Every A/B test, no matter the tool, answers the same question at some point in the request lifecycle: which variation does this specific visitor see. What changes is where that question gets answered.

Three architectures for deciding a variation, by where the decision happensClient-side testing decides in the browser after the original page starts rendering, which causes flicker. Origin server testing decides in a single backend region before sending HTML, which removes flicker but keeps a full round trip to that region. Edge testing decides at a nearby CDN point of presence before the response leaves the network, removing flicker with a shorter round trip.VvisitorClient-side (browser)JS decides after original page rendersRisk: flicker (FOOC)original flashes before rewriteVOrigin server (single region)backend decides before sending HTMLNo flickerbut every visitor detours to that regionVEdge (nearby CDN node)decides before response leaves the networkNo flicker, shorter tripdecided close to the visitor
Edge testing is not a fourth, unrelated category. It is server-side decision logic running at a point of presence close to the visitor instead of a single distant region, which is why it inherits the no-flicker property while changing the latency profile.

Client-side testing runs a script in the browser that rewrites the already-rendered page, which is what causes the flash of original content covered in depth in our client-side vs server-side comparison. A traditional server-side test moves that decision into the backend, before any HTML ships, which removes flicker entirely but still routes every visitor to wherever that backend physically runs. Edge testing keeps the “decide before sending HTML” property of server-side testing, then relocates the decision itself to a CDN point of presence near the visitor, one of hundreds Cloudflare, Fastly, Vercel, and AWS each operate globally.

Why zero flicker is structural, not a feature

Flicker exists only because the browser renders some version of the page before the testing script finishes deciding and rewriting the DOM. Edge computing removes that gap the same way any server-side test does: the edge worker or middleware function evaluates the variation, builds or forwards the correct response, and the browser never receives a version it has to discard and replace. Cloudflare documents this pattern directly in its own Workers example for A/B testing, where the worker reads an assignment cookie and routes the request to the correct backend path before any content reaches the visitor, with no client-side rewrite step at all, according to Cloudflare Workers documentation.

Vercel makes the same tradeoff explicit in its own guidance: running the decision in Edge Middleware, ahead of the response, “reduces layout shift by preventing client-loaded experiments” and avoids shipping the extra JavaScript a client-side testing script requires, according to Vercel own knowledge base. Fastly frames it the same way in its Compute A/B testing documentation, describing the goal as serving the final, already-decided response directly from the edge rather than manipulating the DOM after the fact.

What edge computing adds on top of “no flicker”: proximity

The part specific to edge, versus a traditional single-region server-side test, is where that decision physically happens. A backend that lives in one AWS region answers every visitor from that region, no matter how far they are from it. An edge worker answers from whichever point of presence is nearest the visitor making the request. Cloudflare states its own network spans 348 cities across 8 regions, with 95 percent of the world internet-connected population within 50 milliseconds of one of its data centers, according to Cloudflare network page. That proximity is the entire latency argument for edge experimentation: not that edge compute itself is instant, but that the network hop to reach it is short almost everywhere.

AWS Lambda@Edge, built on top of CloudFront, runs under tighter constraints than a regular Lambda function specifically because it executes on the request path at the edge: functions triggered on the viewer request or viewer response event are capped at 128MB of memory, a fraction of the 10,240MB available to functions on the origin request or origin response event, and both event types cap out at a 30 second execution timeout, well below the 15 minute ceiling of a standard Lambda function, according to AWS own CloudFront developer guide comparing CloudFront Functions and Lambda@Edge. That memory ceiling is not a limitation to sidestep, it is a signal about what edge compute is for: a fast decision made on every request, not a place to run a heavy statistical significance calculation, which belongs in the analysis pipeline, not the request path.

Native geo-targeting, without a second round trip

One capability edge platforms offer natively, and client-side or single-region server-side testing cannot without extra requests, is geo information already resolved by the network itself, before your code runs. Cloudflare Workers expose this through the request cf object, which includes the visitor two-letter country code, region, city, continent, and even isEUCountry, all populated by Cloudflare’s own edge network at request time, according to Cloudflare Workers runtime API documentation. A worker can read that value and pick a variation, a currency, or a locale, in the same pass that decides the A/B test, with no separate geolocation API call and no added latency for that lookup.

That matters specifically for tests that combine an experiment with a geography rule, showing a regional pricing variant only to visitors in one country, or testing a currency format that should never leak outside its target region. Client-side testing can approximate this with a geolocation API call from the browser, which adds a round trip and a dependency on a third-party service; a distant single-region server can also read geo headers, but only after the request has already traveled to that region. At the edge, the geo data and the routing decision arrive together, at the point closest to the visitor.

Comparing the edge platforms, without picking a favorite

There is no universally best edge runtime, only the one that fits the stack a team already has. A neutral snapshot of what each vendor documents about its own platform:

Platform Runtime model Real strength Real limitation
Cloudflare Workers V8 isolates, no cold-start container, KV/Durable Objects for state Runs independently of any hosting provider; broad network footprint (348 cities per Cloudflare’s own network page); mature A/B testing example in official docs Isolate model means no long-lived local file system; state needs KV or Durable Objects, an added piece of infrastructure to reason about
Vercel Edge Middleware V8-based Edge Runtime, tightly integrated with Next.js Simplest path if the app already deploys on Vercel; official templates for A/B testing ship ready to fork Tied to the Vercel platform and its deployment model; less useful if the app is hosted elsewhere
AWS Lambda@Edge AWS Lambda functions running at CloudFront edge locations Fits naturally into an existing CloudFront and AWS stack; reuses familiar Lambda tooling Tightest limits of the four: 128MB memory ceiling on viewer-request and viewer-response triggers (versus 10,240MB for origin-request triggers), plus added cold-start latency at that trigger point, per AWS own CloudFront developer guide
Fastly Compute WebAssembly sandbox (Rust, JavaScript, and other Wasm-targeting languages) Documented, dedicated A/B testing and personalization example with millisecond-range cold starts; strong isolation between tenants Smaller ecosystem and hiring pool than Workers or Lambda; WebAssembly toolchain adds a build step most JavaScript-only teams are not used to

Every row in that table is a snapshot of what each vendor states about its own product today; runtime limits, regional footprint, and free-tier terms change often enough that it is worth checking each platform’s current documentation before committing to one.

The risks nobody mentions in the pitch

Edge experimentation solves flicker and cuts latency, but it introduces failure modes that a client-side snippet or a simple server render never has to deal with, because they all trace back to one thing: a CDN cache that was not built with your specific variation logic in mind.

Cache poisoning: the wrong visitor gets the wrong variant

cache key = URL (+ variant, + country, + device, if you remember to add them)

A CDN cache decides whether two requests are “the same” using a cache key, normally just the URL. If your edge worker varies the response by an assignment cookie, a country, or a device type, but the cache key still ignores all of that, the CDN can store the first visitor personalized response and serve that exact same response to every other visitor who requests that URL, regardless of which variant or region they belong to. PortSwigger own web cache poisoning research describes the underlying mechanism precisely: any difference in a response triggered by an input the cache does not include in its key can be stored and replayed to other users, turning the cache itself into the delivery mechanism for the wrong content. In an A/B test, that means the entire experiment can silently collapse into “everyone sees whatever the first request happened to get,” with no error thrown anywhere.

The fix mirrors the same principle Cloudflare documents for its own Cache Rules: any request header that changes the response, a variant cookie, an Accept-Language value, a device type, needs to be reflected in the cache key or the origin Vary configuration, or the response needs to bypass cache entirely, according to Cloudflare cache documentation on Vary handling. Left unconfigured, Cloudflare’s default behavior does not automatically key on every header your origin varies by, the origin has to declare Vary explicitly and the cache rule has to be told to honor it, which is precisely the gap that turns a personalization mistake into a cache poisoning incident.

Cache invalidation multiplies with every dimension you add

Illustrative example: cached copies multiply with every cache-key dimensionIllustrative example, not a measured statistic. A single page cached once by URL alone becomes 2 cached copies once a 2-variant test is added, 6 once 3 regions are added, and 12 once 2 device types are added on top, because each dimension multiplies the number of distinct cached objects.distinct cached objects for one pagecache-key dimensions stacked1URL only2+ 2 variants6+ 3 regions12+ 2 device types
Illustrative example. Every dimension you fold into the cache key (variant, geo, device) multiplies the number of distinct objects the CDN has to store and invalidate, which is why purging “the page” after a test ends can mean purging dozens of entries, not one.

Once a variant is part of the cache key, “invalidate this page” stops meaning one object and starts meaning every combination of variant, region, and device that has ever been served for that URL. A test that ships a new control after declaring a winner, or that needs an emergency rollback, has to purge every one of those cached combinations, not the single cached copy a static page would have. Teams that do not plan for this ahead of time discover it the same way: they roll back a losing variant in the edge worker, and a slice of visitors keeps seeing the old, cached, losing version until every keyed combination naturally expires or gets purged by hand.

Compute cost is billed differently than a static CDN cache hit

Serving a cached, static response from a CDN is close to free at the traffic levels most sites operate at. Running a worker on every request, to read a cookie, resolve geo, and pick a variant, is not: Cloudflare Workers, Fastly Compute, Lambda@Edge, and Vercel Edge Middleware all bill based on requests and compute time, following each vendor’s own published pricing model, rather than the flat egress-and-cache-hit economics of a plain CDN. That is rarely expensive for a single test, but it is a real, recurring line item that a client-side snippet or a purely static page does not carry, and it is worth checking each platform’s current pricing page before assuming an edge test is “free because it is just a CDN.”

When edge experimentation earns its complexity, and when it is over-engineering

Situation Fits edge experimentation Fits client-side or plain server-side better
Test only swaps a headline, image, or CTA on a few pages Rarely worth it A lightweight snippet or a simple server render solves it with far less operational surface
Zero tolerance for flicker on a high-traffic, globally distributed audience Yes, this is the core use case N/A, this is exactly what edge testing exists to solve
Test needs to vary by country or region before the page is assembled Yes, native geo data removes an extra lookup Client-side geolocation adds a round trip; single-region server-side adds distance
Small team with no one who owns cache-key and edge-worker maintenance Usually not worth it yet Simpler architecture reduces the chance of a silent cache poisoning bug
Test touches pricing, permissions, or business logic already living server-side Worth it if that backend already sits behind a CDN with edge compute A traditional server-side test may already be enough without moving to the edge

The honest rule of thumb: edge experimentation is a latency and flicker optimization on top of server-side testing, not a separate discipline you reach for by default. If a plain server-side test already meets the bar, covered step by step in our server-side implementation guide, moving that same logic to the edge is worth doing only when the extra proximity, or the native geo data, actually changes the outcome for your visitors.

A minimal architecture, described end to end

A typical edge experiment follows the same shape across Cloudflare Workers, Vercel Edge Middleware, and Fastly Compute, differing mostly in syntax. The edge function intercepts the incoming request before it reaches the origin or the cache. It checks for an existing assignment cookie; if none exists, it assigns the visitor to a variant using a stable, deterministic method (a random draw persisted in a cookie, in the pattern Cloudflare’s own A/B testing example follows) so the same visitor lands on the same variant across repeat requests. It optionally reads the network-provided geo data to apply a regional rule in the same pass. It then either rewrites the request path to fetch a different origin response, as Cloudflare’s example does, or assembles the response directly at the edge, as Fastly’s Compute example demonstrates. Finally, and this is the step teams most often skip, it ensures the response either bypasses cache or carries a cache key that reflects every dimension the response actually varies by, so the CDN never conflates two visitors who were supposed to see different things. This mirrors progressive rollout practice in one more way: start the geo or variant rule scoped narrowly, confirm cache behavior is correct for that narrow slice, then widen it, rather than shipping a global edge rule and discovering a cache-key gap once traffic is already flowing through it.

Automate This with Donnu

Everything above happens before Donnu A/B ever gets involved. Donnu is, on purpose, a client-side experimentation tool: a lightweight snippet that runs in the browser, built to install in minutes without an edge worker, a cache-key audit, or a CloudFront distribution to maintain. That is a deliberate tradeoff, not an oversight, edge computing solves a latency and flicker problem that most marketing and CRO tests never actually hit, and it introduces exactly the cache-poisoning and invalidation risk this guide just walked through.

What does not change based on where the decision happens, browser, origin, or edge, is the part most edge testing write-ups skip entirely: deciding a winner with actual statistical rigor. A perfectly zero-flicker edge test still produces a meaningless result if it stops before reaching sample size, or if someone peeks at the dashboard and calls the test early. Donnu focuses on that layer: calculated sample size, an honest Bayesian read of significance, and stable variant assignment, the same discipline that matters whether the variation shipped from a CDN edge node or a snippet in the page. If your test is a visual change and flicker is an acceptable, well-mitigated cost, a free 14-day trial gets a rigorously analyzed experiment running today. If your case genuinely needs edge or server-side infrastructure first, start with our guides on client-side vs server-side testing and how to implement server-side A/B testing to map the architecture before adding a statistics layer on top of it.

References

Read also: The complete guide to feature flags, client-side vs server-side A/B testing, how to implement server-side A/B testing, and progressive rollouts and canary releases.

Frequently asked questions

What makes edge A/B testing different from client-side or server-side testing?
The difference is where the variation decision is made. Client-side testing decides in the visitor browser, after the original page has already started rendering, which is what causes flicker. Traditional server-side testing decides in your backend, in a single region, before any HTML is sent, which removes flicker but keeps the round trip to wherever that backend lives. Edge testing decides at a CDN point of presence physically close to the visitor, before the response leaves the network, combining the no-flicker property of server-side testing with a shorter round trip than a distant origin.
Does edge experimentation really eliminate flicker (FOOC)?
Yes, by the same construction that removes flicker in any server-side test: the variation is chosen and baked into the response before it reaches the browser, so there is no original version to flash on screen first. What edge computing adds on top is proximity, the decision happens at a nearby point of presence instead of a single distant region, which is a latency argument, not an additional flicker fix.
What is cache poisoning risk in an edge-based A/B test, and how do you avoid it?
If your edge worker varies the response per visitor (by variant, region, or device) but the CDN cache key still treats every request to that URL as identical, the cache can store one visitor personalized response and serve it to a completely different visitor. The fix is including every dimension that changes the response, such as the variant cookie, country, or device type, in the cache key or the Vary configuration, or bypassing cache entirely for personalized routes, following the same unkeyed input principle documented in PortSwigger own web cache poisoning research.
Is edge experimentation overkill for a typical marketing site?
Often, yes. If a test only swaps a headline or a CTA on a handful of pages, a lightweight client-side snippet or a plain server-side render usually solves it with far less engineering than deploying and maintaining edge worker code, tracking cache-key correctness, and monitoring compute cost per request. Edge experimentation earns its complexity when flicker is unacceptable, latency truly matters at global scale, or geo-targeting needs to happen before the page is assembled, not by default for every test.
Which edge platform should I use: Cloudflare Workers, Vercel Edge Middleware, Lambda@Edge, or Fastly Compute?
It depends on where your app already lives and what you need from the runtime. Cloudflare Workers and Fastly Compute are the most portable choices for teams not already tied to one hosting stack. Vercel Edge Middleware is the natural fit if the app is already deployed on Vercel, particularly with Next.js. AWS Lambda@Edge fits teams already standardized on CloudFront and the wider AWS stack, with the tightest execution limits of the four. None is universally better, each is documented directly by its vendor and worth comparing against your existing infrastructure before adopting a new one.