Middleware has historically run on the Edge runtime, which has Web APIs but not Node's crypto. jsonwebtoken needs Node's crypto. Your API routes run on Node, that's why they work.
Cleanest fix: use jose, which is built on Web Crypto and works in both runtimes:
import { jwtVerify } from "jose";
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
export async function middleware(req: NextRequest) {
const token = req.cookies.get("session")?.value;
if (!token) return NextResponse.redirect(new URL("/login", req.url));
try {
await jwtVerify(token, secret);
return NextResponse.next();
} catch {
return NextResponse.redirect(new URL("/login", req.url));
}
}Which Next version are you on? From 15.5 you can set export const config = { runtime: "nodejs" } in middleware, and in Next 16 the file is proxy.ts and runs on Node by default. But jose is the smaller change and works everywhere.