Picture of the author

Loading...

Shahin | <h2>Why Use a Content Security Policy?</h2><br> <p>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.</p><br> <h2>What Is a Nonce?</h2><br> <p>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.</p><br> <h2>Adding a Nonce with Proxy</h2><br> <p>Use <code>proxy.ts</code> to generate a nonce and inject it into the CSP header:</p> <pre class="code-block"><code>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 }</code></pre><br> <h3>Proxy Matcher Configuration</h3><br> <p>Exclude static assets and prefetches from CSP enforcement:</p> <pre class="code-block"><code>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' }, ], }, ], }</code></pre><br> <h2>How Nonces Work in Next.js</h2><br> <ul> <li>Proxy generates a nonce and sets it in the CSP and <code>x-nonce</code> headers</li> <li>Next.js extracts the nonce from the CSP header during rendering</li> <li>Nonce is automatically applied to: <ul> <li>React and Next.js runtime scripts</li> <li>Page-specific JavaScript bundles</li> <li>Inline styles/scripts generated by Next.js</li> <li><code>&lt;Script&gt;</code> components using the <code>nonce</code> prop</li> </ul> </li> </ul><br> <h2>Forcing Dynamic Rendering</h2><br> <p>To support nonce injection, pages must be dynamically rendered. Use <code>connection()</code> to wait for a request:</p> <pre class="code-block"><code>import { connection } from 'next/server' export default async function Page() { await connection() // Page content }</code></pre><br> <h2>Reading the Nonce in Server Components</h2><br> <p>Use <code>headers()</code> to access the nonce:</p> <pre class="code-block"><code>import { headers } from 'next/headers' import Script from 'next/script' export default async function Page() { const nonce = (await headers()).get('x-nonce') return ( &lt;Script src="https://www.googletagmanager.com/gtag/js" strategy="afterInteractive" nonce={nonce} /&gt; ) }</code></pre><br> <h2>Static vs Dynamic Rendering with CSP</h2><br> <p>Nonce-based CSP requires dynamic rendering. This disables:</p> <ul> <li>Static optimization</li> <li>Incremental Static Regeneration (ISR)</li> <li>CDN caching</li> <li>Partial Prerendering (PPR)</li> </ul><br> <h3>Performance Implications</h3><br> <ul> <li>Slower initial page loads</li> <li>Increased server load</li> <li>Higher hosting costs</li> </ul><br> <h2>When to Use Nonces</h2><br> <ul> <li>Strict security policies prohibit <code>'unsafe-inline'</code></li> <li>Your app handles sensitive data</li> <li>You need selective inline script execution</li> <li>Compliance requires strict CSP enforcement</li> </ul><br> <h2>Conclusion</h2><br> <p>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.</p><br>