May 11, 2026

Defeating XSS Attacks: Strict Content Security Policy (CSP) in Next.js

By Paresh Prajapati • Lead Architect

Defeating XSS Attacks: Strict Content Security Policy (CSP) in Next.js

The Danger of Inline Scripts

Cross-Site Scripting (XSS) remains one of the most critical vulnerabilities in modern web applications. If an attacker manages to inject a malicious script into your B2B SaaS platform—perhaps through an unescaped comment forum or a compromised third-party NPM package—they can hijack user sessions, steal HttpOnly cookies, and deface your application.

React automatically escapes text output, which provides baseline protection. However, if you rely on third-party analytics, marketing scripts, or dangerously set inner HTML, your Next.js application is still vulnerable. To build an impenetrable frontend at Smart Tech Devs, we must implement a Strict Content Security Policy (CSP) with Nonces.

What is a Strict CSP?

A Content Security Policy is an HTTP header sent by your server that tells the browser exactly which scripts, images, and styles are allowed to execute. A Strict CSP takes this further by rejecting all inline scripts unless they carry a unique, cryptographically secure string called a "nonce" (Number Used Once), generated fresh on every single page load.

If a hacker injects <script>stealData()</script>, the browser will block it entirely because the script lacks the server-generated nonce for that specific HTTP request.

Architecting CSP Nonces in Next.js Middleware

To implement this in the Next.js App Router, we use Edge Middleware to generate the nonce, append it to the CSP header, and pass it down to our React components.

Step 1: Generating the Nonce in Middleware


// middleware.ts
import { NextRequest, NextResponse } from 'next/server';

export function middleware(request: NextRequest) {
    // 1. Generate a random, cryptographically secure Base64 string
    const nonce = Buffer.from(crypto.randomUUID()).toString('base64');

    // 2. Define the Strict CSP Policy
    // We strictly allow our own domain and scripts that carry the exact nonce
    const cspHeader = `
        default-src 'self';
        script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
        style-src 'self' 'nonce-${nonce}';
        img-src 'self' blob: data:;
        font-src 'self';
        object-src 'none';
        base-uri 'self';
        form-action 'self';
        frame-ancestors 'none';
        upgrade-insecure-requests;
    `.replace(/\s{2,}/g, ' ').trim();

    // 3. Clone the request headers and append the CSP and the Nonce
    const requestHeaders = new Headers(request.headers);
    requestHeaders.set('x-nonce', nonce);
    requestHeaders.set('Content-Security-Policy', cspHeader);

    // 4. Return the response with the strict headers attached
    const response = NextResponse.next({
        request: {
            headers: requestHeaders,
        },
    });

    response.headers.set('Content-Security-Policy', cspHeader);
    return response;
}

Step 2: Consuming the Nonce in the Root Layout

Now that the middleware has attached the nonce to the request headers, we must read it in our root layout.tsx and apply it to Next.js's internal script injection.


// app/layout.tsx
import { headers } from 'next/headers';
import Script from 'next/script';

export default function RootLayout({ children }: { children: React.ReactNode }) {
    // Retrieve the nonce securely generated by the middleware
    const nonce = headers().get('x-nonce') || '';

    return (
        <html lang="en">
            <body>
                {children}

                {/* Example of a verified third-party script using the nonce */}
                <Script 
                    src="https://trusted-analytics.com/script.js" 
                    strategy="afterInteractive"
                    nonce={nonce} 
                />
            </body>
        </html>
    );
}

The Engineering ROI

Implementing a Strict CSP is the hallmark of enterprise frontend security. It acts as an absolute fail-safe. Even if a developer makes a mistake and introduces an XSS vulnerability into a React component, the browser itself will refuse to execute the malicious code. Security by design means eliminating entire classes of vulnerabilities at the architectural level.

Paresh Prajapati
Lead Architect, Smart Tech Devs