Next.js Curriculum
HardSecurity100% Free35 min read

Content Security Policy (CSP) & Nonces in Next.js

The 30-Second Interview Answer

Content Security Policy (CSP) prevents Cross-Site Scripting (XSS) and data injection attacks by restricting which scripts and resources can execute. In Next.js App Router, strict CSP is implemented using cryptographically random per-request nonces generated in Middleware/Proxy, passed via request headers, and applied to inline scripts and Next.js internal Flight hydration scripts.

Learning Objectives

Construct a strict Content Security Policy (CSP) header with script-src nonces.

Generate cryptographically secure nonces in Middleware and pass them via x-nonce headers.

Read nonces in root layout.tsx and attach them to <Script> and Next.js internal scripts.

Distinguish between build-time static routes and dynamic routes requiring per-request nonces.

Prerequisites & Background Knowledge
Content Security Policy (CSP) directivesCross-Site Scripting (XSS) attacksHTTP Headers

Why Interviewers Care

Advanced enterprise security requirement mandated in banking, healthcare, and enterprise SaaS applications.

The Challenge

How do you implement a strict Content Security Policy (CSP) with nonces in Next.js App Router? Demonstrate generating a nonce in Middleware and consuming it in the root layout.

Code Architecture & Implementation

1// middleware.ts
2import { NextResponse } from 'next/server';
3import type { NextRequest } from 'next/server';
4
5export function middleware(request: NextRequest) {
6 // 1. Generate a cryptographically random 16-byte base64 nonce
7 const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
8
9 // 2. Build strict Content Security Policy
10 const cspHeader = `
11 default-src 'self';
12 script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
13 style-src 'self' 'nonce-${nonce}';
14 img-src 'self' blob: data: https:;
15 font-src 'self';
16 object-src 'none';
17 base-uri 'self';
18 form-action 'self';
19 frame-ancestors 'none';
20 upgrade-insecure-requests;
21 `.replace(/\s{2,}/g, ' ').trim();
22
23 // 3. Set header on request so Server Components can read it
24 const requestHeaders = new Headers(request.headers);
25 requestHeaders.set('x-nonce', nonce);
26 requestHeaders.set('Content-Security-Policy', cspHeader);
27
28 const response = NextResponse.next({
29 request: {
30 headers: requestHeaders,
31 },
32 });
33
34 // 4. Set CSP header on outgoing HTTP response
35 response.headers.set('Content-Security-Policy', cspHeader);
36
37 return response;
38}

Architectural Explanation

A strict CSP header instructs browsers to block any script not explicitly bearing an authorized cryptographic nonce attribute: <script nonce="...">. In Next.js, Middleware generates a unique base64 nonce using crypto.randomUUID() for every HTTP request. It sets this nonce in the Content-Security-Policy header and forwards it via x-nonce. The root layout reads the nonce via headers() and passes it to custom scripts, while Next.js automatically injects it into framework scripts.

Common Mistakes & Anti-Patterns

Using 'unsafe-inline' in script-src without nonces, nullifying XSS protection.

Reusing the same static nonce across multiple requests, allowing attackers to bypass CSP.

Forgetting that reading `headers()` in root layout makes all routes dynamic (unless using PPR or static CSP hashes).

Interview Follow-Up Traps

The interviewer asks: "Can you use nonces with completely static SSG pages?" No! A static page is generated once at build time; nonces must be unique per request. For static sites, hash-based CSP (`sha256-...`) is used instead.

Frequently Asked Questions

What is the purpose of 'strict-dynamic' in CSP?

'strict-dynamic' allows a script that was loaded with a valid nonce to load other scripts dynamically without requiring separate nonces for them.

Use the `Content-Security-Policy-Report-Only` header to log violations to an endpoint before enforcing blocks.

Yes, passing the nonce to the root layout automatically secures Next.js inline style tags.