Edge auth in Astro 6: JWT verification without a server round-trip
A client came in with 300ms TTFB on /dashboard. Diagnosis: every request was firing a SELECT to the DB to verify the session.
src/middleware.ts in Astro 6 deploys to the edge and intercepts every request before it hits the origin. JWT verification is CPU-only; you verify against the secret already in the runtime's environment variables, without touching the network:
import { defineMiddleware } from "astro:middleware";
import { verifyJWT } from "./lib/auth";
export const onRequest = defineMiddleware(async (context, next) => {
const token = context.cookies.get("session")?.value;
if (!token && context.url.pathname.startsWith("/dashboard")) {
return context.redirect("/login");
}
if (token) {
const payload = verifyJWT(token); // synchronous, no await
if (!payload) return context.redirect("/login");
context.locals.user = payload;
}
return next();
});verifyJWT makes no network calls. With jsonwebtoken, verify() without a callback is synchronous. With jose, jwtVerify() returns a Promise; you can await it anyway because it's pure CPU with no I/O. Either way the result comes back in ~1ms.
For context.locals.user to have correct types in your pages, add this to src/env.d.ts:
declare namespace App {
interface Locals {
user?: { id: string; email: string; role: string };
}
}When edge doesn't help
If you're using opaque session tokens (a UUID mapped to a DB record), your middleware ends up looking like this:
// This gives you the same 200ms, just at the edge now
const sessionId = context.cookies.get("session")?.value;
const user = await db.findBySession(sessionId); // 80-200ms DB round-tripTo keep the advantage, the lookup needs to go to a KV store on the same runtime. In Astro 6 with the Cloudflare adapter, binding access uses the native module:
import { env } from "cloudflare:workers";
const sessionId = context.cookies.get("session")?.value;
const user = await env.KV.get(`session:${sessionId}`);
// 2-5ms instead of 80-200msOn Vercel Edge the pattern differs (Edge Config SDK). The logic is the same; the store access API changes.
The difference isn't network speed — it's dependency type. JWT: the secret is already in environment variables, no network call. Opaque session ID: always needs to look something up externally. If that place is a DB in Virginia and you're in Asunción, the edge won't save you.