使用Cloudflare Workers实现边缘A/B测试
本文详解如何使用Cloudflare Workers在边缘节点实现A/B测试,无需修改源服务器代码。
传统A/B测试 vs 边缘A/B测试
| 方案 | 实现位置 | 延迟 | 准确度 |
|---|---|---|---|
| 服务器端(PHP/Node) | 源服务器 | 高(增加服务器负载) | 高 |
| 客户端(JavaScript) | 浏览器 | 中(闪烁问题) | 低(JS被阻止) |
| 边缘(Cloudflare Workers) | 边缘节点(全球300+) | 极低(< 10ms) | 高 |
基础A/B测试实现
Worker代码(两个版本)
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
// 1. 读取Cookie(已参与过实验的用户保持相同版本)
const cookie = request.headers.get('Cookie') || ''
let variant = getVariantFromCookie(cookie)
// 2. 新用户随机分配
if (!variant) {
variant = Math.random() < 0.5 ? 'A' : 'B'
}
// 3. 从源服务器获取页面
let response = await fetch(request)
// 4. 修改HTML(注入变体内容)
let html = await response.text()
if (variant === 'A') {
html = html.replace('<h1>欢迎</h1>', '<h1>欢迎(版本A - 绿色按钮)</h1>')
html = html.replace('class="btn"', 'class="btn btn-green"')
} else {
html = html.replace('<h1>欢迎</h1>', '<h1>欢迎(版本B - 红色按钮)</h1>')
html = html.replace('class="btn"', 'class="btn btn-red"')
}
// 5. 设置Cookie(记住用户变体)
const newResponse = new Response(html, {
headers: {
'Content-Type': 'text/html',
'Set-Cookie': `ab_variant=${variant}; Max-Age=604800; Path=/`,
'Cache-Control': 'no-cache',
},
})
return newResponse
}
function getVariantFromCookie(cookie) {
const match = cookie.match(/ab_variant=(A|B)/)
return match ? match[1] : null
}
高级A/B测试(多变量 + 统计分析)
三个变量测试
async function handleRequest(request) {
const cookie = request.headers.get('Cookie') || ''
let variant = getVariantFromCookie(cookie)
if (!variant) {
// 随机分配:A(33%) B(33%) C(34%)
const rand = Math.random()
if (rand < 0.33) variant = 'A'
else if (rand < 0.66) variant = 'B'
else variant = 'C'
}
let response = await fetch(request)
let html = await response.text()
// 版本A:原价
if (variant === 'A') {
// 不修改
}
// 版本B:限时折扣
else if (variant === 'B') {
html = html.replace('¥199', '¥149 <span class="badge">限时折扣</span>')
}
// 版本C:免费试用
else {
html = html.replace('立即购买', '免费试用7天')
}
return new Response(html, {
headers: {
'Content-Type': 'text/html',
'Set-Cookie': `ab_variant=${variant}; Max-Age=604800; Path=/`,
},
})
}
发送数据到Google Analytics
// 在Worker中发送GA4事件
async function sendGAEvent(variant, request) {
const response = await fetch('https://www.google-analytics.com/mp/collect', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
client_id: getClientId(request),
events: [{
name: 'ab_test_view',
params: {
variant: variant,
test_name: 'homepage_button_color',
},
}],
}),
})
}
function getClientId(request) {
const ip = request.headers.get('CF-Connecting-IP')
const ua = request.headers.get('User-Agent')
// 简单哈希作为client_id(或使用Cookie)
return btoa(ip + ua).slice(0, 20)
}
边缘KV存储(持久化计数)
使用Cloudflare KV
// 在Cloudflare Dashboard中创建KV namespace
// 绑定到Worker:Variable name = AB_TEST_KV
async function handleRequest(request) {
const variant = Math.random() < 0.5 ? 'A' : 'B'
// 记录展示次数
let impressions = await AB_TEST_KV.get(variant + '_impressions') || 0
await AB_TEST_KV.put(variant + '_impressions', parseInt(impressions) + 1)
// 获取页面并修改
let response = await fetch(request)
let html = await response.text()
if (variant === 'A') {
html = html.replace('注册', '开始免费试用')
} else {
html = html.replace('注册', '立即注册(限时优惠)')
}
return new Response(html, {
headers: {
'Content-Type': 'text/html',
'Set-Cookie': `ab_variant=${variant}; Max-Age=604800`,
},
})
}
读取KV统计(管理API)
// 在另一个Worker或API路由中
app.get('/ab-stats', async (request) => {
const variantA = await AB_TEST_KV.get('A_impressions') || 0
const variantB = await AB_TEST_KV.get('B_impressions') || 0
return new Response(JSON.stringify({
variant_A: parseInt(variantA),
variant_B: parseInt(variantB),
}), {
headers: { 'Content-Type': 'application/json' },
})
})
完整的A/B测试Dashboard
使用Cloudflare Workers + HTML
// worker.js - A/B测试Dashboard
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url)
// API:获取统计数据
if (url.pathname === '/api/stats') {
const stats = {
A: {
impressions: await AB_TEST_KV.get('A_impressions') || 0,
conversions: await AB_TEST_KV.get('A_conversions') || 0,
},
B: {
impressions: await AB_TEST_KV.get('B_impressions') || 0,
conversions: await AB_TEST_KV.get('B_conversions') || 0,
},
}
return new Response(JSON.stringify(stats), {
headers: { 'Content-Type': 'application/json' },
})
}
// Dashboard页面
const html = `
<!DOCTYPE html>
<html>
<head>
<title>A/B Test Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<h1>A/B Test Results</h1>
<canvas id="chart"></canvas>
<script>
fetch('/api/stats')
.then(r => r.json())
.then(data => {
new Chart(document.getElementById('chart'), {
type: 'bar',
data: {
labels: ['Version A', 'Version B'],
datasets: [{
label: 'Conversions',
data: [data.A.conversions, data.B.conversions],
}],
},
})
})
</script>
</body>
</html>
`
return new Response(html, {
headers: { 'Content-Type': 'text/html' },
})
}
边缘A/B测试的优势
| 优势 | 说明 |
|---|---|
| 零服务器负载 | 在Cloudflare边缘节点执行,不消耗源服务器资源 |
| 全球低延迟 | 300+ 边缘节点,用户从最近的节点获取变体 |
| 无闪烁 | 在HTML发送到浏览器之前就完成替换 |
| 绕过广告拦截器 | 修改HTML,不依赖JavaScript |
| 实时调整流量比例 | 通过KV存储动态调整分配比例 |
实战:WordPress集成
在WordPress中查看A/B测试结果
// 在WordPress后台添加Dashboard Widget
add_action('wp_dashboard_setup', function() {
wp_add_dashboard_widget('ab_test_widget', 'A/B Test Results', 'display_ab_stats');
});
function display_ab_stats() {
// 从Cloudflare API获取统计数据
$stats = wp_remote_get('https://your-worker.your-subdomain.workers.dev/api/stats', [
'headers' => [
'Authorization' => 'Bearer ' . get_option('cloudflare_api_token'),
],
]);
$data = json_decode(wp_remote_retrieve_body($stats));
echo '<table class="widefat">';
echo '<tr><th>版本</th><th>展示</th><th>转化</th><th>转化率</th></tr>';
foreach (['A', 'B'] as $v) {
$impressions = $data->$v->impressions;
$conversions = $data->$v->conversions;
$rate = $impressions > 0 ? round($conversions / $impressions * 100, 2) : 0;
echo "<tr><td>$v</td><td>$impressions</td><td>$conversions</td><td>$rate%</td></tr>";
}
echo '</table>';
}
发布Worker到Cloudflare
使用Wrangler CLI
# 安装Wrangler
npm install -g wrangler
# 登录Cloudflare
wrangler login
# 创建Worker项目
wrangler init ab-test-worker
# 编辑 src/index.js(粘贴上面的Worker代码)
# 创建KV namespace
wrangler kv:namespace create "AB_TEST_KV"
# 更新 wrangler.toml
# kv_namespaces = [
# { binding = "AB_TEST_KV", id = "..." }
# ]
# 发布
wrangler publish
在Cloudflare Dashboard中配置
1. 登录 https://dash.cloudflare.com/
2. Workers & Pages → Create Application
3. 选择 "Create Worker"
4. 粘贴Worker代码
5. 部署
6. 在 "Triggers" 中设置路由(如 example.com/*)
A/B测试最佳实践
- 样本量足够:每个变体至少1000次展示才具有统计意义
- 测试周期:至少运行1-2周(包含工作日和周末)
- 单变量测试:一次只测试一个元素(按钮颜色 OR 文案,不能同时测试)
- 统计显著性:使用卡方检验或在线计算器验证结果(p-value < 0.05)
- 避免工作日偏差:不要在周一上线测试、周五就停止
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 两个版本都显示 | Cookie未正确设置 | 检查Set-Cookie头 |
| 统计数据不准确 | 爬虫也计入展示 | 在Worker中过滤爬虫UA |
| 转化率异常高 | 重复计数 | 使用IP+User-Agent哈希去重 |
| KV写入失败 | 免费额度限制 | 升级到Workers Paid Plan |
通过Cloudflare Workers实现A/B测试,无需修改服务器代码,延迟极低,是2026年最先进的A/B测试方案。
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

评论(0)