How to Secure a Next.js App Built with AI
If you built your app with v0, Bolt, Cursor, or Lovable, there is a strong chance it runs on Next.js. It is a fantastic framework, but it has a specific security model, server versus client, middleware versus route handlers, that AI tools rarely respect. The code compiles and the page loads, so the gaps stay invisible until someone probes them. Internal audits suggest around 45% of AI-generated code contains at least one OWASP Top 10 vulnerability, and Next.js apps are no exception.
Here are the seven things that matter most when you secure an AI-built Next.js app.
1. Never rely on middleware as your only auth check
In March 2025, the Next.js world got a wake-up call: CVE-2025-29927, a CVSS 9.1 authorization-bypass flaw. By adding a single spoofed header, x-middleware-subrequest, an attacker could make Next.js skip middleware entirely, bypassing any authentication or authorization enforced there. Protected admin routes became reachable with no credentials at all.
It was patched in 12.3.5, 13.5.9, 14.2.25, and 15.2.3, so step one is simple: stay on a patched version. But the deeper lesson outlives the CVE: middleware is for edge-level routing, not your security boundary.
The fix: update Next.js, and never treat middleware as your only gate. Re-check authentication and authorization inside every protected route and action.
2. Treat Server Actions like the public API they are
Server Actions feel like calling a local function, you add use server and call it from a component. But under the hood, every Server Action is a public POST endpoint that anyone can hit with any arguments. AI tools, which default to trusting the request payload, routinely generate actions that assume the caller is who they claim to be.
That is exactly how Broken Object-Level Authorization (BOLA / IDOR) sneaks in: an action takes an id and acts on it without checking that the current user actually owns that record.
The fix: treat every Server Action and route handler like a public API. Verify the user's identity, and that they are allowed to touch this specific record, every time, on the server.
3. Validate every input at runtime with Zod
TypeScript types vanish at runtime. A userId: string annotation enforces nothing once the code is running, so any Server Action or API route that trusts its TypeScript types is trusting input it never actually checked.
The fix: validate every incoming payload with a runtime schema library like Zod (z.object({...}).parse(input)). This blocks malformed data, injection, and type-confusion attacks before your logic runs.4. Keep secrets out of NEXT_PUBLIC_
Next.js has one rule that trips up vibe coders constantly: any environment variable prefixed with NEXT_PUBLIC_ is inlined into the browser bundle. If you put a secret key behind that prefix to "make it work," you have just shipped it to every visitor.
The fix: only publishable values get NEXT_PUBLIC_. Secret keys stay server-side, read in route handlers or server components, never exposed to the client. (More in our API keys and secrets guide.)5. Don't hand user input to dangerouslySetInnerHTML
React escapes output by default, which prevents most cross-site scripting (XSS). But two patterns punch a hole straight through that protection: dangerouslySetInnerHTML and direct innerHTML assignment. AI tools reach for them to render rich text or markdown, and pass user content in unsanitized.
The fix: avoid dangerouslySetInnerHTML for anything a user can influence. If you must render HTML, sanitize it first with a library like DOMPurify.6. Don't forget CSRF and cookies
Next.js Server Actions automatically compare the Origin and Host headers, which blocks most cross-site request forgery. Custom API routes that change state do not get that for free.
The fix: for state-changing API routes, verify the Origin header or use a CSRF token, and setSameSite=Lax(orStrict) on your session cookies as a second layer.
7. Stay patched
Next.js ships regular security releases, and the framework's most serious issues, like CVE-2025-29927, are fixed in updates. An app frozen on an old version inherits every flaw found since.
The fix: keep Next.js and your dependencies current, and watch the release notes for security fixes.
The Next.js pre-launch checklist
- Running a Next.js version patched against CVE-2025-29927.
- Auth and authorization re-checked inside every route handler and Server Action, not just middleware.
- Every Server Action treated as a public endpoint, with ownership checks on every record.
- All inputs validated at runtime with Zod or similar.
- No secrets behind
NEXT_PUBLIC_; secret keys read only on the server. - No user-controlled content passed to
dangerouslySetInnerHTMLwithout sanitizing. - CSRF defenses on state-changing API routes;
SameSitecookies set.
Why a scanner won't catch most of this
A scanner can flag an outdated Next.js version. It cannot tell whether your Server Action checks that the logged-in user owns the order they are editing, that is business logic, and it is exactly where AI-built apps break. Finding those flaws takes a human who tries to act like an attacker. (For the wider list of what gets missed, see the 7 flaws AI coding tools ship by default.)
If you want a real test of your Next.js app before launch, a human pentest starts at $499. See a sample report first.
Frequently asked questions
- Is Next.js secure?
- Next.js is secure when you use its security model correctly. The framework gives you the tools, but it does not enforce good decisions: middleware is not a security boundary, Server Actions are public endpoints, and NEXT_PUBLIC_ variables ship to the browser. Re-check authorization inside every route and action, validate inputs, keep secrets server-side, and stay on a patched version.
- What is CVE-2025-29927?
- It is a critical (CVSS 9.1) authorization-bypass vulnerability in Next.js disclosed in March 2025. By sending a spoofed x-middleware-subrequest header, an attacker could make Next.js skip middleware entirely, bypassing any authentication or authorization enforced there. It was fixed in 12.3.5, 13.5.9, 14.2.25, and 15.2.3, so the first mitigation is simply to update.
- Are Next.js Server Actions safe?
- Only if you treat them like public APIs. A Server Action looks like a local function, but under the hood it is a public POST endpoint that anyone can call with any arguments. You must verify the caller's identity, confirm they are allowed to act on the specific record, and validate every input - every time, on the server.
- Why can't I put an API key in a NEXT_PUBLIC_ variable?
- Because any environment variable prefixed with NEXT_PUBLIC_ is inlined into the client JavaScript bundle and shipped to every visitor. That is fine for publishable values, but a secret key behind that prefix is effectively published. Keep secret keys server-side and read them only in route handlers or server components.