Next.js cookbook · Advanced

Add a Content Security Policy with nonces in Next.js

Mitigate XSS by allowing only scripts that Next.js itself renders.

Approach

Generate a random nonce per request in the request interceptor (proxy.ts/middleware.ts), set a strict Content-Security-Policy header using 'nonce-…' and 'strict-dynamic', and pass the nonce via a request header. Next.js applies the nonce to its own scripts automatically; read it with headers() for your inline scripts. Nonces require dynamic rendering.

Solution

// proxy.ts (middleware.ts on Next.js 15)
import { NextResponse, type NextRequest } from "next/server";

export function proxy(request: NextRequest) {
  const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
  const isDev = process.env.NODE_ENV === "development";
  const csp = [
    "default-src 'self'",
    `script-src 'self' 'nonce-${nonce}' 'strict-dynamic'${isDev ? " 'unsafe-eval'" : ""}`,
    `style-src 'self' 'nonce-${nonce}'`,
    "img-src 'self' blob: data: https://cdn.examplecms.com",
    "font-src 'self'",
    "object-src 'none'",
    "base-uri 'self'",
    "form-action 'self'",
    "frame-ancestors 'none'",
    "upgrade-insecure-requests",
  ].join("; ");

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set("x-nonce", nonce);
  requestHeaders.set("Content-Security-Policy", csp);
  const response = NextResponse.next({ request: { headers: requestHeaders } });
  response.headers.set("Content-Security-Policy", csp);
  return response;
}

export const config = {
  matcher: [{ source: "/((?!api|_next/static|_next/image|favicon.ico).*)", missing: [{ type: "header", key: "next-router-prefetch" }] }],
};

// In a Server Component that needs an inline script:
// const nonce = (await headers()).get("x-nonce") ?? undefined;  <Script nonce={nonce} ... />

Pitfalls

  • Using 'unsafe-inline' for scripts (defeats the purpose).
  • Expecting nonces on statically generated pages (they must be dynamic).
  • Breaking third-party scripts — start with Content-Security-Policy-Report-Only.

Variations

  • Hash-based CSP for fully static sites.
  • Report violations with report-to to monitor before enforcing.

Further reading: Next.js: Content Security Policy

Reference: https://nextjs.org/docs/app/guides/content-security-policy

More Next.js recipes