你已经知道边缘计算能降低延迟,但真正让 Edge Middleware 成为生产级架构核心的,不是「快」这个字——而是它能在请求到达你的源站之前,完成认证、路由、个性化和实验分流等关键决策。根据 Vercel 2025 年度报告,使用 Edge Middleware 的应用平均首字节时间(TTFB)降低了 47%,而 Cloudflare 的数据显示,边缘认证比传统中心化认证的 P99 延迟低 3-5 倍。但大多数开发者把 Edge Middleware 当成一个简单的 URL 重写工具,白白浪费了它最强大的能力。本文将用真实代码拆解 5 个生产级模式,帮你把 Edge Middleware 用到极致。
🔐 一、边缘认证:在请求到达源站前拦截未授权访问
1.1 为什么认证应该在边缘完成?
传统的认证流程是:请求 → 源站 → 验证 Token → 返回响应。在全球部署的场景下,一个亚洲用户访问美东源站的认证请求,仅网络往返就需要 200-300ms。如果把认证逻辑移到边缘节点,这个延迟可以降到 5-20ms。
但边缘环境有严格的限制——无状态、执行时间短(通常 < 50ms)、不能访问传统数据库。这意味着你的认证方案必须适配这些约束。
⚠️ **警告:**永远不要在 Edge Middleware 中做数据库查询或调用慢速外部服务。边缘节点的 CPU 时间预算极其有限,超时会直接返回错误。
1.2 JWT 验证 + JWKS 缓存的边缘实现
最常见的边缘认证模式是 JWT 验证。关键挑战是:如何在边缘获取和缓存签名密钥(JWKS)?
// Cloudflare Worker: 边缘 JWT 认证中间件
// 使用 Web Crypto API 验证 JWT,无需第三方依赖
const JWKS_URL = 'https://auth.example.com/.well-known/jwks.json';
const CACHE_TTL = 3600; // 1 小时缓存
export default {
async fetch(request, env) {
const authResult = await authenticateRequest(request, env);
if (!authResult.success) {
return new Response(JSON.stringify({ error: authResult.error }), {
status: 401,
headers: { 'Content-Type': 'application/json' }
});
}
// 将用户信息注入请求头,传递给源站
const modifiedRequest = new Request(request);
modifiedRequest.headers.set('X-User-Id', authResult.userId);
modifiedRequest.headers.set('X-User-Roles', JSON.stringify(authResult.roles));
return fetch(modifiedRequest);
}
};
async function authenticateRequest(request, env) {
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return { success: false, error: 'Missing token' };
}
const token = authHeader.slice(7);
try {
// 解码 JWT header 获取 kid
const [headerB64] = token.split('.');
const header = JSON.parse(atob(headerB64));
// 从缓存或远程获取 JWKS
const jwks = await getJWKS(env);
const key = jwks.keys.find(k => k.kid === header.kid);
if (!key) return { success: false, error: 'Unknown key' };
// 使用 Web Crypto API 导入公钥
const publicKey = await crypto.subtle.importKey(
'jwk', key,
{ name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
false, ['verify']
);
// 验证签名
const [headerPart, payloadPart, signaturePart] = token.split('.');
const encoder = new TextEncoder();
const data = encoder.encode(`${headerPart}.${payloadPart}`);
const signature = Uint8Array.from(atob(signaturePart), c => c.charCodeAt(0));
const valid = await crypto.subtle.verify('RSASSA-PKCS1-v1_5', publicKey, signature, data);
if (!valid) return { success: false, error: 'Invalid signature' };
// 解析 payload 并检查过期
const payload = JSON.parse(atob(payloadPart));
if (payload.exp && payload.exp < Date.now() / 1000) {
return { success: false, error: 'Token expired' };
}
return { success: true, userId: payload.sub, roles: payload.roles || [] };
} catch (e) {
return { success: false, error: 'Token verification failed' };
}
}
// 使用 Cloudflare KV 缓存 JWKS,避免每次请求都远程获取
async function getJWKS(env) {
const cached = await env.JWKS_CACHE.get('jwks', { type: 'json' });
if (cached) return cached;
const response = await fetch(JWKS_URL);
const jwks = await response.json();
await env.JWKS_CACHE.put('jwks', JSON.stringify(jwks), { expirationTtl: CACHE_TTL });
return jwks;
}
1.3 基于路径的认证策略
不是所有路径都需要认证。一个常见的模式是「白名单 + 默认拒绝」:
// Vercel Edge Middleware: 基于路径的认证策略
// middleware.ts 放在项目根目录
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
// 公开路径白名单
const PUBLIC_PATHS = [
'/api/health',
'/api/auth/login',
'/api/auth/register',
'/login',
'/register',
'/about',
'/pricing',
];
// 路径前缀白名单
const PUBLIC_PREFIXES = [
'/_next/', // Next.js 静态资源
'/images/', // 图片资源
'/favicon', // favicon
'/robots.txt',
'/sitemap.xml',
];
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// 检查是否为公开路径
if (PUBLIC_PATHS.includes(pathname)) {
return NextResponse.next();
}
// 检查是否为公开前缀
if (PUBLIC_PREFIXES.some(prefix => pathname.startsWith(prefix))) {
return NextResponse.next();
}
// 验证认证 token
const token = request.cookies.get('session_token')?.value;
if (!token) {
// API 路径返回 401,页面路径重定向到登录
if (pathname.startsWith('/api/')) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const loginUrl = new URL('/login', request.url);
loginUrl.searchParams.set('redirect', pathname);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
💡 **提示:**Vercel Edge Middleware 运行在 Edge Runtime 上,不支持 Node.js API(如
fs、crypto)。如果你需要 crypto 操作,使用 Web Crypto API 或在 middleware 中只做轻量级的 token 格式校验,将真正的签名校验留给 API Route。
🚀 二、A/B 测试与渐进式发布:边缘分流的正确姿势
2.1 为什么 A/B 测试应该在边缘做?
传统的 A/B 测试通常在客户端(JavaScript)实现,这会导致页面闪烁(Flicker)——用户先看到原始版本,然后才切换到实验版本。在边缘做分流可以确保用户从第一次请求开始就看到正确的版本,同时避免客户端 A/B 测试脚本对性能的影响。
根据 Google 的研究,页面加载每增加 100ms,转化率下降 1%。客户端 A/B 测试脚本通常增加 50-200ms 的加载时间,而边缘分流几乎零开销。
2.2 Cookie 持久化的边缘分流实现
// Cloudflare Worker: A/B 测试边缘分流
// 确保同一用户始终看到同一个实验版本
const EXPERIMENTS = {
'new-checkout': {
variants: ['control', 'variant-a', 'variant-b'],
weights: [0.34, 0.33, 0.33], // 流量分配权重
cookieName: 'exp_new_checkout',
cookieMaxAge: 60 * 60 * 24 * 30, // 30 天
},
'pricing-page-v2': {
variants: ['control', 'variant'],
weights: [0.5, 0.5],
cookieName: 'exp_pricing_v2',
cookieMaxAge: 60 * 60 * 24 * 14, // 14 天
}
};
export default {
async fetch(request) {
const url = new URL(request.url);
const cookies = parseCookies(request.headers.get('Cookie') || '');
const experimentResults = {};
for (const [expName, config] of Object.entries(EXPERIMENTS)) {
// 检查是否已有实验 cookie(用户已分组)
const existingVariant = cookies[config.cookieName];
if (existingVariant && config.variants.includes(existingVariant)) {
experimentResults[expName] = existingVariant;
} else {
// 新用户:按权重随机分配
experimentResults[expName] = assignVariant(config.variants, config.weights);
}
}
// 构建修改后的请求
const modifiedRequest = new Request(request);
// 将实验分组信息注入请求头
modifiedRequest.headers.set('X-Experiments', JSON.stringify(experimentResults));
// 如果需要改写路径(如将 /pricing 指向 /pricing-v2)
const variant = experimentResults['pricing-page-v2'];
if (url.pathname === '/pricing' && variant === 'variant') {
const newUrl = new URL('/pricing-v2', url.origin);
modifiedRequest.headers.set('X-Rewrite-To', newUrl.pathname);
}
const response = await fetch(modifiedRequest);
// 设置实验 cookie(确保后续请求保持同一版本)
const newResponse = new Response(response.body, response);
for (const [expName, variant] of Object.entries(experimentResults)) {
const config = EXPERIMENTS[expName];
const existingCookie = cookies[config.cookieName];
if (existingCookie !== variant) {
newResponse.headers.append(
'Set-Cookie',
`${config.cookieName}=${variant}; Path=/; Max-Age=${config.cookieMaxAge}; SameSite=Lax; Secure`
);
}
}
// 添加调试头(生产环境可移除)
newResponse.headers.set('X-Experiments', JSON.stringify(experimentResults));
return newResponse;
}
};
// 按权重随机分配变体
function assignVariant(variants, weights) {
const random = Math.random();
let cumulative = 0;
for (let i = 0; i < variants.length; i++) {
cumulative += weights[i];
if (random < cumulative) return variants[i];
}
return variants[variants.length - 1];
}
function parseCookies(cookieStr) {
return Object.fromEntries(
cookieStr.split(';').map(c => c.trim().split('=').map(s => s.trim()))
);
}
📌 **记住:**A/B 测试的 cookie 必须设置
HttpOnly和Secure标志。在上面的示例中,实验 cookie 不需要 HttpOnly(因为前端 JS 也可能需要读取),但生产环境中应根据安全需求调整。
2.3 分流方案对比
| 方案 | 延迟影响 | 闪烁问题 | SEO 影响 | 实现复杂度 | 推荐场景 |
|---|---|---|---|---|---|
| ✅ Edge Middleware 分流 | < 5ms | 无 | 可控制 | 中 | 服务端渲染页面、需要 SEO 的页面 |
| ✅ 客户端 JS 分流 | 50-200ms | 有 | 有风险 | 低 | SPA 应用、不需要 SEO 的内部页面 |
| ⚠️ DNS 分流 | 0ms | 无 | 复杂 | 高 | 地理级别的大范围实验 |
| ❌ 源站分流 | 50-300ms | 无 | 可控制 | 低 | 不推荐(增加源站负载) |
🌍 三、地理路由与请求改写:基于位置的智能分发
3.1 地理感知路由的核心逻辑
Edge Middleware 可以读取用户的地理位置信息(通过 cf-ipcountry、x-vercel-ip-country 等头部),实现基于位置的内容分发、语言切换和合规处理。
// Cloudflare Worker: 地理感知路由与内容改写
// 支持多语言、GDPR 合规、CDN 回源优化
const LOCALE_MAP = {
'CN': { locale: 'zh-CN', currency: 'CNY', redirect: '/zh' },
'TW': { locale: 'zh-TW', currency: 'TWD', redirect: '/zh-tw' },
'HK': { locale: 'zh-HK', currency: 'HKD', redirect: '/zh-hk' },
'JP': { locale: 'ja', currency: 'JPY', redirect: '/ja' },
'KR': { locale: 'ko', currency: 'KRW', redirect: '/ko' },
'US': { locale: 'en', currency: 'USD', redirect: '/en' },
'GB': { locale: 'en-GB', currency: 'GBP', redirect: '/en' },
'DE': { locale: 'de', currency: 'EUR', redirect: '/de' },
};
// GDPR 适用国家列表
const GDPR_COUNTRIES = new Set([
'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR',
'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL',
'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE',
]);
export default {
async fetch(request) {
const url = new URL(request.url);
const country = request.headers.get('cf-ipcountry') || 'US';
const localeConfig = LOCALE_MAP[country] || LOCALE_MAP['US'];
// 1. 未手动选择语言的用户:自动重定向
const acceptLang = request.headers.get('Accept-Language') || '';
const userCookie = parseCookies(request.headers.get('Cookie') || '');
const hasManualLocale = userCookie['locale_preference'];
if (!hasManualLocale && url.pathname === '/') {
return Response.redirect(`${url.origin}${localeConfig.redirect}`, 302);
}
// 2. GDPR 合规:注入 consent 提示
const isGDPR = GDPR_COUNTRIES.has(country);
// 3. 注入地理信息到请求头
const modifiedRequest = new Request(request);
modifiedRequest.headers.set('X-User-Country', country);
modifiedRequest.headers.set('X-User-Locale', localeConfig.locale);
modifiedRequest.headers.set('X-User-Currency', localeConfig.currency);
modifiedRequest.headers.set('X-GDPR-Required', isGDPR ? 'true' : 'false');
// 4. 价格 API 请求:改写为对应货币
if (url.pathname.startsWith('/api/prices')) {
url.searchParams.set('currency', localeConfig.currency);
return fetch(new Request(url.toString(), modifiedRequest));
}
const response = await fetch(modifiedRequest);
// 5. GDPR 场景:注入 Consent 头部
if (isGDPR) {
const newResponse = new Response(response.body, response);
newResponse.headers.set('X-Consent-Required', 'true');
return newResponse;
}
return response;
}
};
function parseCookies(cookieStr) {
return Object.fromEntries(
cookieStr.split(';').map(c => c.trim().split('=').map(s => s.trim()))
);
}
3.2 限流与安全防护:边缘级别的 DDoS 缓解
在边缘做限流的优势是:恶意流量在到达源站之前就被拦截。以下是基于滑动窗口的边缘限流实现:
// Cloudflare Worker: 基于滑动窗口的边缘限流
// 使用 Durable Objects 实现精确计数
export class RateLimiter {
constructor(state, env) {
this.state = state;
this.env = env;
}
async fetch(request) {
const { identifier, limit, windowMs } = await request.json();
const now = Date.now();
const windowStart = now - windowMs;
// 获取当前窗口的请求记录
let requests = (await this.state.storage.get(identifier)) || [];
// 清除过期记录
requests = requests.filter(timestamp => timestamp > windowStart);
if (requests.length >= limit) {
const retryAfter = Math.ceil((requests[0] + windowMs - now) / 1000);
return new Response(JSON.stringify({
allowed: false,
remaining: 0,
retryAfter,
}), {
status: 429,
headers: {
'Content-Type': 'application/json',
'Retry-After': String(retryAfter),
}
});
}
// 记录本次请求
requests.push(now);
await this.state.storage.put(identifier, requests);
return new Response(JSON.stringify({
allowed: true,
remaining: limit - requests.length,
resetAt: new Date(requests[0] + windowMs).toISOString(),
}), {
headers: { 'Content-Type': 'application/json' }
});
}
}
💡 四、Edge Middleware 的性能陷阱与避坑指南
4.1 常见的性能反模式
在生产环境中,Edge Middleware 最容易踩的坑不是功能实现,而是性能优化。以下是我在实际项目中总结的 5 个高频反模式:
- ❌ 在 middleware 中做外部 HTTP 调用 — 每个外部调用增加 50-200ms 延迟,直接抵消边缘部署的优势
- ❌ 在 middleware 中解析完整的请求体 — 大请求体(如文件上传)会消耗大量 CPU 时间
- ❌ 使用 npm 包而未检查 bundle 大小 — 某些 npm 包在边缘环境中会导致 cold start 时间暴增
- ❌ 在 middleware 中写日志到远程服务 — 同步日志写入会阻塞请求处理
- ❌ 不设置 KV/Cache 缓存 — 每次请求都做 JWT 验证中的 JWKS 获取
4.2 最佳实践清单
- ✅ 缓存一切可缓存的数据 — JWKS、配置、feature flags 都应该缓存在 KV 或 Cache API 中
- ✅ 使用 Web Crypto API — 边缘环境原生支持,无需引入第三方加密库
- ✅ 保持 middleware 代码 < 1MB — 超过此大小会显著增加 cold start 时间
- ✅ 使用 early return 模式 — 对公开路径快速放行,减少不必要的处理
- ✅ 添加
X-Edge-Timing头 — 测量 middleware 执行时间,持续监控性能
⚡ **关键结论:**Edge Middleware 的最佳实践可以用一句话概括——能不在边缘做的事情就不要在边缘做。边缘的价值在于快速决策和路由,而不是复杂业务逻辑。把认证、限流、A/B 分流这些「决策型」逻辑放在边缘,把数据处理、业务计算留给源站。
🔧 五、总结与工具推荐
Edge Middleware 不是银弹,但它确实为 Web 应用架构提供了一个新的优化维度。核心价值在于:在请求到达源站之前完成关键决策,从而降低延迟、提升安全性和改善用户体验。
适用场景总结:
| 场景 | 适用度 | 说明 |
|---|---|---|
| ✅ JWT 认证与权限校验 | 高 | 轻量级验证,无需数据库 |
| ✅ A/B 测试分流 | 高 | 无闪烁,零客户端开销 |
| ✅ 地理路由与语言切换 | 高 | 利用边缘节点的地理信息 |
| ✅ 请求限流与安全防护 | 高 | 恶流量在边缘拦截 |
| ⚠️ 个性化内容渲染 | 中 | 需要用户数据时可能需要 KV 查询 |
| ❌ 复杂业务逻辑 | 低 | CPU 时间限制,留给源站 |
| ❌ 数据库密集型操作 | 低 | 边缘无法直连传统数据库 |
推荐工具与平台:
- 🔧 Cloudflare Workers — 最成熟的边缘计算平台,Durable Objects 支持有状态边缘逻辑
- 🔧 Vercel Edge Middleware — Next.js 生态首选,零配置集成
- 🔧 Deno Deploy — 原生 TypeScript 支持,全球 35+ 区域
- 🔧 Fastly Compute — 基于 WebAssembly 的边缘计算,适合高性能场景
- 🔧 Edge Config(Vercel) — 超低延迟的全球配置存储,适合 feature flags
💡 **提示:**如果你的项目已经在用 Next.js,从 Vercel Edge Middleware 开始是最小阻力路径。如果需要更灵活的控制和更低的成本,Cloudflare Workers 是更好的选择。两者都支持在 < 50ms 内完成认证、路由和分流决策。