Verified against Claude Code · 2026-07-11
Write Next.js middleware that gates routes without slowing down every request
Produces a middleware.ts auth guard scoped with a real matcher config, doing only a light session check at the edge instead of a full database round trip on every request.
The prompt
Ready to copy — highlighted parts are example details you can swap.
<context> Routes that need protection: /dashboard/* and /settings/*, but not /dashboard/public-status How a valid session is identified: A signed JWT in a session cookie named __session, verified with the app's edge-compatible secret Where to send unauthenticated users: /login </context> <task> Write middleware.ts to gate the protected routes above. Scope it with a matcher config so it only runs on routes that actually need it — never a blanket middleware that runs on every request including static assets and public pages. </task> <constraints> - Middleware runs on the Edge runtime by default, on every matched request, before any page renders — treat that as a hard latency and API-surface budget. Do only a light check here: read a session cookie/token and verify its signature/expiry. Do not make a database call, an external API call, or anything else with meaningful latency inside middleware. - If deeper validation is needed (checking the session against a revocation list, loading full user permissions), do that light-then-heavy split: middleware does the cheap signature/expiry check and redirects obviously-invalid requests immediately; the actual page or a layout does the heavier check for anything that passed the cheap gate. - Use the matcher config (or an explicit path check) to exclude _next/static, _next/image, favicon, and any public routes — don't rely on checking pathname manually inside the middleware body for exclusions that the matcher can handle declaratively. - On redirect, preserve the original destination (e.g. as a callbackUrl query param) so the user lands back where they meant to go after authenticating. </constraints> <output_format> The middleware.ts file with its config.matcher, followed by one sentence explaining what the middleware does NOT check (and where that heavier check actually lives instead). </output_format>
Customize the highlighted detailsoptional — the prompt above already works
Why this works
Middleware's biggest practical risk isn't getting the auth logic wrong, it's putting the wrong kind of work in it: because middleware runs on every matched request on the Edge runtime before any caching or rendering happens, a database call or third-party API check placed there adds that latency to every single navigation, not just the ones that need deep validation. The explicit light-then-heavy split — cheap signature/expiry check in middleware, anything expensive pushed to a layout or page that only runs for requests that already passed the cheap gate — is the concrete fix, and stating it as a rule stops the model from reaching for the more 'thorough-looking' single-function version that happens to be slow. Requiring the matcher config instead of manual pathname checks also matters operationally: an unscoped middleware silently runs on _next/static and _next/image requests too, adding latency to every asset load on the site, not just page navigations.
What you get back
config.matcher: ['/dashboard/:path*', '/settings/:path*']. Middleware verifies the __session JWT's signature and expiry only; it does NOT check whether the user's role still has access to a specific settings sub-page — that permission check happens in app/settings/layout.tsx, which does have database access and only runs for requests middleware already let through.
Verified against
Claude Code Sonnet 4.6 · 2026-07-11
Cursor 2.1 · 2026-07-16
Changelog
- 2026-07-11 — Initial version, tested against Claude Code on Next.js 16 middleware with edge JWT verification.
Need this built into your business?
If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.
EXPLORE CUSTOM SOFTWARE

