Back to Blog
Cloud NativeJune 26, 20264 min read

Edge-Side A/B Testing: Routing Traffic Between Vercel and Cloudflare Workers

Master edge-side A/B testing by routing traffic between Vercel and Cloudflare Workers. Learn how to perform traffic splitting for seamless canary releases.

Edge ComputingVercelCloudflare WorkersA/B TestingServerlessDevOpsCloudflareDeployment

When we needed to validate a major architectural change for our frontend, we couldn't afford the risk of a full-scale deployment. We wanted to run an experiment: route 20% of our production traffic to a new Vercel-hosted version while keeping the rest on our stable Cloudflare-hosted build. Implementing edge-side A/B testing this way allowed us to measure performance metrics in real-world scenarios without impacting our entire user base.

The Problem with Client-Side Redirects

Initially, we tried using client-side JavaScript to fetch different versions of the app. It was a disaster. The "flicker" effect was noticeable, and our SEO rankings took a small hit because search engine crawlers were getting confused by the dynamic content injection. We needed something faster, something that happened before the request even reached our origin servers. That’s where Cloudflare Workers shine as a global traffic router.

Architecting the Edge Router

Since we were already using Deploying Next.js 16 to Cloudflare Workers with OpenNext, we decided to leverage that infrastructure. By placing a Cloudflare Worker in front of both our Vercel deployment and our existing Cloudflare-hosted site, we could inspect incoming requests and route them based on a cookie or a random hash.

Here is how the request flow looks:

Flow diagram: User → Cloudflare Worker; Cloudflare Worker → Route A Vercel Edge Functions; Cloudflare Worker → Route B Cloudflare Workers Site; Vercel Edge Functions → Response; Cloudflare Workers Site → Response

Implementing Traffic Splitting

The logic itself is straightforward. We use a simple cookie-based persistence strategy to ensure that once a user is bucketed into a specific experiment group, they stay there. If no cookie exists, we assign them to a bucket using a random number generator.

Here is a snippet of the routing logic we implemented in our index.js worker file:

JAVASCRIPT
export default {
  async fetch(request, env) {
    const cookie = request.headers.get(CE9178">'cookie') || CE9178">'';
    let bucket = cookie.includes(CE9178">'exp_group=B') ? CE9178">'B' : null;

    if (!bucket) {
      // 20% traffic to Vercel (Group B), 80% to Cloudflare (Group A)
      bucket = Math.random() < 0.2 ? CE9178">'B' : CE9178">'A';
    }

    const url = new URL(request.url);
    if (bucket === CE9178">'B') {
      url.hostname = CE9178">'vercel-app.example.com';
    } else {
      url.hostname = CE9178">'cloudflare-app.example.com';
    }

    const newRequest = new Request(url, request);
    const response = await fetch(newRequest);
    
    // Persist the group assignment
    const newResponse = new Response(response.body, response);
    newResponse.headers.append(CE9178">'Set-Cookie', CE9178">`exp_group=${bucket}; Path=/; Max-Age=3600`);
    return newResponse;
  }
};

Comparing Deployment Targets

When you're routing traffic between two different platforms, you have to account for environment parity. We found that Next.js Traffic Shadowing: Architecting Canary Releases at the Edge provided a good baseline, but managing two separate build pipelines requires constant vigilance.

FeatureVercel Edge FunctionsCloudflare Workers
Global LatencyExcellentExceptional
Cold StartsMinimalZero
Binding SupportVercel KV/BlobKV/D1/R2
Ease of RoutingHigh (via Middleware)High (via Worker)

Lessons Learned

We spent about two days debugging an issue where the Vercel-hosted version was failing to read cookies set by the Cloudflare Worker. It turned out to be a header casing issue—Vercel's edge runtime was expecting specific header formats that our worker wasn't consistently providing.

If I were to do this again, I’d invest more time in Next.js App Router Server-Side Prefetching: Background Cache Warming to ensure that the Vercel-hosted experiment feels just as snappy as the Cloudflare-hosted one. Running edge-side A/B testing successfully isn't just about the routing logic; it's about making sure your edge deployment strategies are perfectly aligned across both providers.

FAQ

Does this routing add significant latency? In our testing, the worker overhead was roughly 15-20ms, which is negligible compared to the benefits of true server-side splitting.

How do I handle session state across platforms? This is the hardest part. We used a shared Redis instance to ensure that session data remains consistent regardless of whether the user hits Vercel or Cloudflare.

Can I use this for more than two versions? Yes, you can extend the Math.random() logic to split traffic into as many buckets as you need, though keeping track of the user experience becomes exponentially more difficult.

Edge-side A/B testing has fundamentally changed how we ship features. By decoupling the routing layer from our application logic, we’ve gained the ability to test risky changes in production with total confidence.

Similar Posts