Guides
How to Protect API Routes in Next.js
An API route is reachable directly, regardless of what the frontend hides — so every route needs to independently validate the caller's identity and permissions, not assume a request only ever comes from your own authenticated frontend.
Prerequisites
- arrow_rightA Next.js project with authentication already set up
- arrow_rightRoute Handlers (app/api/*/route.ts) to secure
Step 1: Validate the Session in the Route Handler
app/api/orders/route.ts
export async function GET(request: Request) {
const session = await getSession(request);
if (!session) {
return new Response('Unauthorized', { status: 401 });
}
// proceed with authorized logic
}Step 2: Check Authorization, Not Just Authentication
Being logged in isn't the same as being allowed to perform a specific action — check that the session's user actually has permission for the requested resource (e.g. this order belongs to this user) before returning or modifying data.
Step 3: Validate Input and Add Rate Limiting
Validate the request body against a schema (Zod is a common choice) before using it, and add rate limiting on any endpoint that's expensive or could be abused (login attempts, AI API calls, email sending).
Common Errors
- arrow_rightAssuming middleware-level auth checks cover API routes too — middleware matchers need to explicitly include API paths, or each route needs its own check
- arrow_rightTrusting a user ID sent in the request body instead of deriving it from the validated session
- arrow_rightNo rate limiting on expensive operations, leaving them open to abuse or runaway cost
Security Considerations
- arrow_rightNever trust client-supplied identifiers for authorization decisions — always derive identity from the verified session
- arrow_rightReturn generic error messages for auth failures — don't reveal whether a user ID exists or not
- arrow_rightLog authentication failures for monitoring, without logging sensitive payload data
Frequently Asked Questions
Is middleware enough to protect API routes?add
Only if its matcher explicitly covers the API paths — many middleware configs are written with pages in mind and accidentally exclude /api routes, leaving them unchecked.
How do I add rate limiting?add
Track request counts per user or IP over a time window, either with a lightweight in-memory approach for small scale or a dedicated service (like Upstash's rate limiter) for anything running across multiple serverless instances.
Should error messages reveal what went wrong?add
For authentication and authorization failures, keep messages generic — detailed error messages can leak information useful to an attacker, like whether a given user account exists.