To secure Clerk + Next.js integration, you need to: (1) configure middleware with proper route matchers and explicit public routes, (2) use auth() in Server Components and API routes to verify users, (3) always use the verified userId for database operations, (4) add webhook routes to publicRoutes so Clerk's callbacks get through, and (5) never trust client-provided user IDs. The whole blueprint rests on one idea: middleware protection and server-side verification aren't alternatives. They work together.
TL;DR
Clerk's middleware handles route protection automatically. Use auth() in Server Components, currentUser() when you need full user data, and configure publicRoutes properly. The userId that auth() hands back is verified server-side, so it's safe to use directly in database queries.
Middleware Configuration Clerk Next.js
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher([
'/',
'/about',
'/blog(.*)',
'/api/webhooks(.*)',
])
export default clerkMiddleware((auth, req) => {
if (!isPublicRoute(req)) {
auth().protect()
}
})
export const config = {
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
}
Protected Server Component
import { auth, currentUser } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'
export default async function Dashboard() {
const { userId } = auth()
if (!userId) {
redirect('/sign-in')
}
// Use userId for database queries
const posts = await db.posts.findMany({
where: { authorId: userId },
})
const user = await currentUser()
return (
<div>
<h1>Welcome, {user?.firstName}</h1>
{posts.map(post => <div key={post.id}>{post.title}</div>)}
</div>
)
}
Protected API Route
import { auth } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'
export async function POST(req: Request) {
const { userId } = auth()
if (!userId) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const body = await req.json()
const post = await db.posts.create({
data: {
title: body.title,
authorId: userId, // Verified Clerk user ID
},
})
return NextResponse.json(post)
}
Middleware protects routes, not data. Verify userId in API routes and Server Components too, and use that verified value for every database operation. Never a client-provided one.
Security Checklist
Pre-Launch Checklist
Middleware configured with correct matchers
Public routes explicitly defined
API routes check auth()
Database queries use verified userId
Webhook routes in publicRoutes
Related Integration Stacks
Auth0 + Next.js Alternative NextAuth + Prisma Self-Hosted OAuth Security Patterns
Check Your Clerk Integration
Scan for authentication security issues.