Home / Guides

Next.js API route without auth: how to protect it

Updated 2026-10-14 ยท Defensive guide for your own app

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

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 scan

FAQ

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.