CSP

Fix CSP Header in Next.js โ€” Nonce-Based Approach

Updated April 2026

Reading this article? Verify your fix in real-time. Scan your Next.js page CSP โ€” CSPFixer โ†’

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