CSP Headers for Next.js Applications
Implement Content Security Policy in Next.js using middleware, next.config.js headers, and nonce injection. Handle App Router, Server Components, and Edge Runtime CSP requirements.
Detailed Explanation
CSP in Next.js
Next.js applications require careful CSP configuration because they combine server-rendered HTML, client-side hydration, and various data-fetching patterns. Next.js provides multiple ways to set CSP headers.
Method 1: Middleware (Recommended)
Middleware allows per-request nonce generation, which is the most secure approach:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
const csp = `
default-src 'self';
script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
style-src 'self' 'nonce-${nonce}';
img-src 'self' data: blob:;
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
`.replace(/\n/g, '');
const response = NextResponse.next();
response.headers.set('Content-Security-Policy', csp);
response.headers.set('x-nonce', nonce);
return response;
}
Method 2: next.config.js Headers
For static policies without nonces:
// next.config.js
module.exports = {
async headers() {
return [{
source: '/(.*)',
headers: [{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
}]
}];
}
};
Accessing the Nonce in Components
In Next.js App Router, pass the nonce through a Server Component:
// app/layout.tsx
import { headers } from 'next/headers';
export default async function RootLayout({ children }) {
const headersList = await headers();
const nonce = headersList.get('x-nonce') ?? '';
return (
<html>
<body>
<script nonce={nonce} />
{children}
</body>
</html>
);
}
Next.js-Specific Challenges
Inline scripts: Next.js injects inline scripts for page data (__NEXT_DATA__) and hydration. These need the nonce attribute, which Next.js 13.4+ supports natively via the nonce prop on <Script>.
next/script component:
import Script from 'next/script';
<Script src="https://analytics.example.com/script.js" nonce={nonce} strategy="afterInteractive" />
Image optimization: If using next/image with external sources, add those domains to img-src:
img-src 'self' data: blob: https://images.unsplash.com
Development mode: Next.js dev server uses eval() for Fast Refresh. Add 'unsafe-eval' only in development:
const isDev = process.env.NODE_ENV === 'development';
const scriptSrc = isDev
? `'self' 'unsafe-eval' 'nonce-${nonce}'`
: `'self' 'nonce-${nonce}' 'strict-dynamic'`;
Vercel Deployment
When deploying to Vercel, middleware runs at the Edge. Use crypto.randomUUID() (available in Edge Runtime) for nonce generation instead of Node.js crypto.randomBytes().
Use Case
Next.js is one of the most popular React frameworks for production applications. Whether building a marketing site, SaaS dashboard, or e-commerce platform with Next.js, implementing CSP through middleware provides the strongest security. The nonce-based approach works seamlessly with Next.js Server Components, App Router, and Vercel Edge deployment.