Skip to content

Next.js CSP Integration

Add CSP headers to your Next.js application using middleware or configuration.

  • Next.js 13+ (App Router or Pages Router)
  • Access to middleware.ts or next.config.js

Create middleware.ts in your project root:

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set(
"Content-Security-Policy-Report-Only",
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; report-uri https://ingest.headerhawk.com/csp/YOUR_SITE_ID"
);
return response;
}
export const config = {
matcher: "/((?!api|_next/static|_next/image|favicon.ico).*)",
};

For static headers without dynamic content:

/** @type {import('next').NextConfig} */
const nextConfig = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "Content-Security-Policy-Report-Only",
value:
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; report-uri https://ingest.headerhawk.com/csp/YOUR_SITE_ID",
},
],
},
];
},
};
module.exports = nextConfig;

For inline scripts, generate nonces per-request:

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
const response = NextResponse.next();
// Set CSP header with nonce
response.headers.set(
"Content-Security-Policy-Report-Only",
`default-src 'self'; script-src 'self' 'nonce-${nonce}'; style-src 'self' 'unsafe-inline'; report-uri https://ingest.headerhawk.com/csp/YOUR_SITE_ID`
);
// Pass nonce to components via header
response.headers.set("x-nonce", nonce);
return response;
}

Access the nonce in Server Components:

import { headers } from "next/headers";
export default async function Page() {
const headersList = await headers();
const nonce = headersList.get("x-nonce") || "";
return (
<script nonce={nonce} dangerouslySetInnerHTML={{ __html: "..." }} />
);
}

Different policies for different routes:

/** @type {import('next').NextConfig} */
const nextConfig = {
async headers() {
return [
{
source: "/admin/:path*",
headers: [
{
key: "Content-Security-Policy",
value: "default-src 'self'; script-src 'self'",
},
],
},
{
source: "/(.*)",
headers: [
{
key: "Content-Security-Policy-Report-Only",
value:
"default-src 'self'; script-src 'self' 'unsafe-inline'; report-uri https://ingest.headerhawk.com/csp/YOUR_SITE_ID",
},
],
},
];
},
};
  1. Restart the Next.js dev server after config changes
  2. Check matcher patterns in middleware
  3. Verify middleware.ts is in the correct location (root or src/)

Some Next.js features require 'unsafe-eval' in development:

const isDev = process.env.NODE_ENV === "development";
const csp = `
default-src 'self';
script-src 'self' ${isDev ? "'unsafe-eval'" : ""};
report-uri https://ingest.headerhawk.com/csp/YOUR_SITE_ID
`;

Check headers in browser DevTools (Network tab) or:

Terminal window
curl -I http://localhost:3000 | grep -i content-security-policy