site logo

Marico's space

零延迟安全:Next.js Edge Middleware 🛡️

前端技术 2026-08-04 20:58:31 25

最近折腾 Next.js 的 Edge Middleware,踩了几个坑,这篇把问题说清楚。

做了这么多年 Web 开发,一直被一个老问题困扰:安全检查到底放在哪里做最合理?传统方案是请求先到中心服务器,验完身份再放行。但如果用户在广州访问托管在北京的服务器,光物理距离就要付出几十毫秒的代价。更憋屈的是,如果用户压根没登录,或者 Token 已经过期,这几十毫秒完全是白跑——服务器浪费算力处理了一个本该在门口就被拦下的请求。

CDN(内容分发网络)解决了静态资源的问题,图片、CSS 这些东西可以缓存到离用户近的节点。但动态逻辑没法缓存——谁来读 Cookie?谁来验签 JWT?谁来查 Redis 查限流?这些活儿必须跑在某个服务器上。直到 Edge Computing(边缘计算) 出现。

我们在给企业级 Next.js 应用做安全架构时,把核心鉴权逻辑全推到了 Edge 层执行。用 Next.js Middleware 跑在 Edge Runtime 上,请求在 CDN 边缘节点就被拦截处理,根本不等到达我们的源站。

V8 Edge Runtime 是什么

Next.js Middleware 不是跑在标准 Node.js 环境里的。标准 Node.js 太重了,启动慢、内存占用大。Middleware 用的是 V8 Edge Runtime,基于 Web API 的轻量级运行环境。它用高度隔离的沙箱环境,启动时间不到 10 毫秒。正因为足够轻量,阿里云 CDN Edge 或者 Cloudflare 这些平台才能把你的 Middleware 部署到全球几百个边缘节点上。

这意味着伦敦的用户请求在伦敦就处理了,悉尼的用户请求在悉尼就处理了。但这份速度是有代价的:你没法用 Node.js 原生 API,比如 fspath,或者那些重量级的 Node 专用加密库。只能用标准的 Web API,比如 fetchWebCrypto

Phase 1:在边缘验证 JWT

Middleware 最强大的用法就是在入口处校验 JWT。用户没登录?直接在边缘重定向到登录页,根本不需要唤醒任何 Server Component,也不用碰主数据库。

因为 Edge Runtime 不支持标准 Node.js 的 JWT 库,我们用 jose——一个零依赖、专为 Edge WebCrypto API 设计的库。


// middleware.ts (Next.js 项目根目录)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { jwtVerify } from 'jose'; export async function middleware(request: NextRequest) { // 1. 获取用户想访问的路由 const pathname = request.nextUrl.pathname; // 2. 只保护特定路由,比如 /dashboard if (pathname.startsWith('/dashboard')) { // 3. 从 HTTP-only Cookie 里取 Token const token = request.cookies.get('enterprise_auth_token')?.value; if (!token) { // 在边缘层直接重定向到登录页 return NextResponse.redirect(new URL('/login', request.url)); } try { // 4. 用 jose 调用 WebCrypto API 验证 JWT 签名 const secret = new TextEncoder().encode(process.env.JWT_SECRET); await jwtVerify(token, secret); // 5. Token 有效,放行到主 Next.js 服务器 return NextResponse.next(); } catch (error) { // Token 过期、被篡改或无效,边缘直接拦下 return NextResponse.redirect(new URL('/login', request.url)); } } return NextResponse.next();
} // 6. 优化执行:只在特定路径上运行这个中间件
export const config = { matcher: ['/dashboard/:path*'],
};

Phase 2:边缘层全局限流

除了身份验证,Edge 还是抵御暴力破解和 DDoS 攻击的绝佳位置。攻击者想每秒狂刷一万次 /api/login 接口?如果让这些请求冲到主数据库,系统直接雪崩。

我们用边缘友好的 Redis 数据库(比如阿里云或腾讯云提供的边缘 Redis 服务)做限流。因为这类服务提供 REST API,我们直接在 Middleware 里用标准 fetch 就能跟它通信。


// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server'; export async function middleware(request: NextRequest) { if (request.nextUrl.pathname === '/api/login') { // 1. 从边缘请求头里拿用户的 IP 地址 const ip = request.ip || request.headers.get('x-real-ip') || 'unknown'; // 2. 通过高性能边缘 Redis 检查限流状态 // 生产环境建议用 @upstash/ratelimit 这类专用限流库 const response = await fetch(`https://edge-redis-provider.com/ratelimit?ip=${ip}`, { headers: { Authorization: `Bearer ${process.env.REDIS_REST_TOKEN}` } }); const { allowed, remaining } = await response.json(); if (!allowed) { // 3. 在边缘直接拦截,返回 429 状态码 // 攻击流量压根碰不到你的主基础设施 return new NextResponse('Too Many Requests. Please slow down.', { status: 429, headers: { 'X-RateLimit-Remaining': remaining.toString() } }); } } return NextResponse.next();
}

收益分析

把 Next.js 应用的安全逻辑搬到 Edge Middleware,在性能和安全性上都是质的飞跃。身份验证、重定向、A/B 测试、限流这些操作全部推到网络边缘,未登录用户或者恶意请求可以零延迟被拦截。源站服务器的算力压力骤降,托管成本跟着降,整个应用对外形成一层密不透风的边缘防护盾。

现代企业 Web 架构的正确分工是:源站只处理纯正的、已验证的、业务相关的逻辑,其他杂活全扔给 Edge 就完事儿了。