← Back to Blog
Next.js2026-05-09·19 min read

War Story: Our Next.js 16 App Was Hit by a CSRF Attack via a Missing Middleware

By ANKUSH CHOUDHARY JOHAL

It was 2 AM on a Tuesday when our on-call engineer pinged the team Slack: “We’re seeing a spike in unauthorized project deletions and settings changes. User sessions are valid, but the requests don’t match user behavior.” We had just launched our Next.js 16 project management SaaS two weeks prior, and this was our first major incident. None of us expected the root cause to be a missing 10-line middleware file.

The Setup: Our Next.js 16 Stack

We built the app using Next.js 16’s App Router, with NextAuth.js v5 for authentication, Prisma as our ORM, and Vercel for hosting. We followed most security best practices: hashed passwords, rate limiting on auth endpoints, input validation on all API routes. But in a pre-launch refactor, we stripped out our custom CSRF middleware to debug a conflicting cookie issue, and never added it back. We assumed SameSite=Lax cookies (the default for NextAuth) would be enough to block cross-site request forgery attacks. We were wrong.

The Attack: How It Happened

CSRF attacks exploit the browser’s automatic cookie-sending behavior. If a user is logged into our app, any request to our domain from a malicious site will include their valid session cookie. Our API routes for mutating data (delete project, update settings) only checked for a valid session, not a CSRF token. An attacker hosted a malicious form on a phishing site that submitted a POST request to our /api/project/delete endpoint. When logged-in users visited the phishing site, their browsers sent the request with their valid session, and we processed it without question.

We didn’t notice at first because the attacker targeted low-traffic accounts initially. By the time we caught it, 14 users had had settings changed, and 3 had lost project data. The kicker? The attacker didn’t even need to bypass our auth, they just used our own session handling against us.

The Fix: Adding CSRF Middleware in Next.js 16

We immediately rolled back to the last known good deployment, then set to work adding CSRF protection. For Next.js 16, we used the next-csrf package, which integrates seamlessly with the App Router’s middleware. Here’s the steps we took:

  1. Installed next-csrf and initialized it with a secret stored in environment variables.
  2. Added a CSRF token generation endpoint at /api/csrf that sets a signed CSRF cookie.
  3. Updated all mutating API routes to check for a valid CSRF token in the request body or headers, rejecting requests without one.
  4. Added middleware that runs on all POST, PUT, DELETE, and PATCH requests to verify the CSRF token against the signed cookie.

We also audited all existing API routes to ensure no mutating endpoints were unprotected. Within 4 hours, we had a patched version deployed, and the malicious requests stopped immediately.

Lessons Learned: Preventing CSRF in Next.js 16

This incident taught us three key lessons for Next.js apps:

  • Never rely solely on SameSite cookies for CSRF protection. SameSite=Lax blocks cross-site POST requests from some contexts, but it’s not foolproof, especially for older browsers or edge cases.
  • Always use CSRF middleware for any mutating API routes. It’s a small addition that blocks the vast majority of CSRF attacks.
  • Add CSRF checks to your pre-launch security audit. We had a checklist, but CSRF wasn’t on it, and that oversight cost us user trust.

We also set up automated security scans to catch missing middleware in future deployments. A missing 10-line file caused a major incident, but it’s a mistake we’ll never make again.