Fix CSP Header in Next.js — Nonce-Based Approach
Next.js generates inline scripts for hydration that a strict CSP will block. Adding unsafe-inline fixes the error but defeats the purpose of CSP entirely. Nonces are the right solution — a random value per request that lets specific scripts run.
Browser Console Error
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash, or a nonce is required to enable inline execution.Step 1 — Generate nonce in middleware.ts
// middleware.ts
import { NextResponse } from 'next/server';
import crypto from 'crypto';
export function middleware(request) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
const csp = [
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`,
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self'",
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
].join('; ');
const response = NextResponse.next();
response.headers.set('Content-Security-Policy', csp);
response.headers.set('x-nonce', nonce); // pass to layout
return response;
}
export const config = { matcher: '/((?!_next/static|_next/image|favicon.ico).*)' };
Step 2 — Read nonce in root layout
// app/layout.tsx
import { headers } from 'next/headers';
export default function RootLayout({ children }) {
const nonce = headers().get('x-nonce') || '';
return (
<html>
<head>
<script
nonce={nonce}
dangerouslySetInnerHTML={{
__html: `window.__NONCE__ = "${nonce}";`
}}
/>
</head>
<body>{children}</body>
</html>
);
}
Step 3 — Pass nonce to Script components
// For next/script components
import Script from 'next/script';
<Script
nonce={nonce}
src="https://example.com/script.js"
strategy="afterInteractive"
/>
Third-party scripts with nonces
For Google Analytics, Hotjar, or any third-party script, pass the nonce and add their domains to script-src:
const csp = [
`script-src 'self' 'nonce-${nonce}' https://www.googletagmanager.com`,
"connect-src 'self' https://www.google-analytics.com",
].join('; ');
Not sure which resources your page is loading? CSPFixer scans your live URL and generates a CSP based on what it finds.
Scan your page CSP live → CSPFixer