~4 min read • Updated Oct 26, 2025
Why Use a Content Security Policy?
CSP helps prevent cross-site scripting (XSS), clickjacking, and other injection attacks by restricting which sources can load content, scripts, styles, fonts, media, and more. It’s a critical layer of defense for modern web applications.
What Is a Nonce?
A nonce is a unique, random string generated per request. It allows specific inline scripts or styles to bypass strict CSP rules. Because the nonce changes with every request, attackers cannot predict or reuse it.
Adding a Nonce with Proxy
Use proxy.ts to generate a nonce and inject it into the CSP header:
export function proxy(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const cspHeader = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
style-src 'self' 'nonce-${nonce}';
img-src 'self' blob: data:;
font-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`.replace(/\s{2,}/g, ' ').trim()
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)
requestHeaders.set('Content-Security-Policy', cspHeader)
const response = NextResponse.next({ request: { headers: requestHeaders } })
response.headers.set('Content-Security-Policy', cspHeader)
return response
}Proxy Matcher Configuration
Exclude static assets and prefetches from CSP enforcement:
export const config = {
matcher: [
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}How Nonces Work in Next.js
- Proxy generates a nonce and sets it in the CSP and
x-nonceheaders - Next.js extracts the nonce from the CSP header during rendering
- Nonce is automatically applied to:
- React and Next.js runtime scripts
- Page-specific JavaScript bundles
- Inline styles/scripts generated by Next.js
<Script>components using thenonceprop
Forcing Dynamic Rendering
To support nonce injection, pages must be dynamically rendered. Use connection() to wait for a request:
import { connection } from 'next/server'
export default async function Page() {
await connection()
// Page content
}Reading the Nonce in Server Components
Use headers() to access the nonce:
import { headers } from 'next/headers'
import Script from 'next/script'
export default async function Page() {
const nonce = (await headers()).get('x-nonce')
return (
<Script
src="https://www.googletagmanager.com/gtag/js"
strategy="afterInteractive"
nonce={nonce}
/>
)
}Static vs Dynamic Rendering with CSP
Nonce-based CSP requires dynamic rendering. This disables:
- Static optimization
- Incremental Static Regeneration (ISR)
- CDN caching
- Partial Prerendering (PPR)
Performance Implications
- Slower initial page loads
- Increased server load
- Higher hosting costs
When to Use Nonces
- Strict security policies prohibit
'unsafe-inline' - Your app handles sensitive data
- You need selective inline script execution
- Compliance requires strict CSP enforcement
Conclusion
Setting a Content Security Policy with nonces in Next.js strengthens your app’s security posture. While it requires dynamic rendering and may impact performance, it’s essential for applications with strict security or compliance needs.
Written & researched by Dr. Shahin Siami