Fix CSP Header in Next.js โ Nonce-Based Approach
Updated April 2026
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