Next.js API route without auth: how to protect it
Short answer
Next.js treats Route Handlers and Server Actions like public API endpoints: anyone can call them directly, whether or not your UI shows a button. Hiding a page or doing a check in a layout or Proxy (middleware) is not enough. Each handler must verify the session and the user's permission itself, close to the data, and return 401 or 403 when it fails.
The fix
Centralise the check in a server-only Data Access Layer, as the Next.js docs recommend:
// app/lib/dal.ts
import 'server-only'
import { cookies } from 'next/headers'
import { decrypt } from '@/app/lib/session'
export async function getSession() {
const cookie = (await cookies()).get('session')?.value
const session = await decrypt(cookie)
return session?.userId ? session : null
}
// app/api/projects/[id]/route.ts
import { getSession } from '@/app/lib/dal'
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
const session = await getSession()
if (!session) return new Response(null, { status: 401 })
const { id } = await params
const project = await db.project.findUnique({ where: { id } })
if (!project || project.ownerId !== session.userId) {
return new Response(null, { status: 403 })
}
await db.project.delete({ where: { id } })
return new Response(null, { status: 204 })
}
Do the same at the top of every 'use server' action. If you use Supabase or another auth library, use its server-side "get user" call instead of the hand-rolled session.
How to check
- List every file under
app/api/**/route.tsandpages/api/, and every'use server'function. Each should call your auth helper before reading or writing data. - Check ownership, not just sign-in: "is logged in" is not "owns this record".
- Routes that call paid APIs (AI, email, SMS) need auth and a rate limit, or anyone can run up your bill.
- Don't rely on Proxy/middleware alone; Next.js describes it as an optimistic check, not your only line of defence.
Check your own project in seconds
Upload a .zip of your own Lovable, Bolt, Cursor, Supabase or Firebase project. ProdCanary reviews the code statically and shows your top 3 risks with plain-English fixes. Your upload is deleted right after the scan.
Run a free scanFAQ
Isn't middleware enough?
No. The Next.js docs say Proxy is useful for optimistic redirects, but security checks should happen as close to the data source as possible.
Does a check in a layout protect child routes?
No. Layouts don't re-render on navigation and don't stop nested segments or Server Actions from running.
Which status code should I return?
401 when there is no valid session, 403 when the user is signed in but not allowed.
Sources
This guide is for securing apps you own or are authorised to work on. Facts above are from the official docs linked; check them for changes.