← All posts
content security policynext.jsCSPweb securityXSS prevention

Add Content Security Policy Next.js: Step-by-Step Guide

O

OverMCP Team

TL;DR: To add Content Security Policy (CSP) headers to your Next.js app, set them via next.config.js using the headers function, or use middleware for dynamic policies. CSP blocks XSS, clickjacking, and data injection by defining trusted sources. For static policies, configure in next.config.js; for dynamic (e.g., inline scripts in getServerSideProps), use middleware or set headers in API routes.

What Is Content Security Policy and Why You Need It in Next.js

Content Security Policy (CSP) is a browser security standard that prevents cross-site scripting (XSS), clickjacking, and other code injection attacks. It works by specifying which sources are allowed to load scripts, styles, images, fonts, and other resources. When a page has a CSP header, the browser blocks resources from unauthorized origins.

In a Next.js app, adding a content security policy next.js implementation is crucial because modern apps often load third-party scripts (analytics, fonts, widgets) and use inline styles/scripts. Without CSP, any injected malicious script can execute freely. Next.js also generates inline script tags for its own hydration and dynamic routing, so you must account for those.

How to Add CSP in next.config.js (Static Policy)

The simplest way to add a Content Security Policy to your Next.js app is via next.config.js. This method is best for static policies that don't change per request.

// next.config.js
const cspHeader = `
  default-src 'self';
  script-src 'self' 'unsafe-eval' 'unsafe-inline';
  style-src 'self' 'unsafe-inline';
  img-src 'self' blob: data:;
  font-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';
  block-all-mixed-content;
  upgrade-insecure-requests;
`;

module.exports = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: cspHeader.replace(/\s{2,}/g, ' ').trim(),
          },
        ],
      },
    ];
  },
};

Note: The 'unsafe-inline' and 'unsafe-eval' are often needed for Next.js development and some third-party scripts, but they weaken security. For production, consider using nonces or hashes (explained below).

Add CSP Using Middleware (Dynamic Policy)

If you need to generate a dynamic CSP (e.g., include a nonce for each request), use Next.js middleware. This is common when you have inline scripts that cannot use hashes.

Step 1: Create Middleware File

// 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' 'unsafe-inline';
    img-src 'self' blob: data:;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    block-all-mixed-content;
    upgrade-insecure-requests;
  `.replace(/\s{2,}/g, ' ').trim();

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-nonce', nonce);

  const response = NextResponse.next({
    request: { headers: requestHeaders },
  });
  response.headers.set('Content-Security-Policy', csp);

  return response;
}

// Optionally limit middleware paths
export const config = {
  matcher: '/((?!api|_next/static|_next/image|favicon.ico).*)',
};

Step 2: Use Nonce in Your Layout or Page

In your root layout or specific page, read the nonce from headers and add it to <script> tags:

// app/layout.tsx
import { headers } from 'next/headers';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const headersList = headers();
  const nonce = headersList.get('x-nonce') || '';

  return (
    <html lang="en">
      <head>
        <script
          nonce={nonce}
          dangerouslySetInnerHTML={{
            __html: `console.log('Hello from inline script');`,
          }}
        />
      </head>
      <body>{children}</body>
    </html>
  );
}

Using CSP Hashes for Inline Scripts

Instead of nonces, you can use hashes. Calculate the SHA hash of your inline script and add it to the policy. This is static and works well for scripts that don't change.

  • Add the inline script to your page (e.g., in layout.js).
  • Compute the hash: echo -n "console.log('Hello');" | openssl dgst -sha256 -binary | base64
  • Add the hash to your CSP: script-src 'sha256-<base64hash>'
  • Example:

    // next.config.js
    const cspHeader = `
      default-src 'self';
      script-src 'self' 'sha256-abc123...';
      ...
    `;

    Testing Your Content Security Policy

    Use browser DevTools to check CSP violations. Open the Console tab; any blocked resources will show a CSP error. You can also use the report-uri or report-to directive to send reports to an endpoint.

    const cspHeader = `
      ...
      report-uri /api/csp-report;
      report-to csp-endpoint;
    `;

    Set up a report endpoint in Next.js API routes:

    // pages/api/csp-report.ts
    export default function handler(req, res) {
      const report = req.body;
      console.log('CSP Violation:', report);
      // Log or send to external service
      res.status(204).end();
    }

    Common Pitfalls with CSP in Next.js

  • Missing nonce for Next.js inline scripts: Next.js injects its own inline scripts for client-side navigation. If you don't allow 'unsafe-inline' or a nonce, these scripts will be blocked, breaking app functionality. Using middleware with a nonce is the safest approach.
  • Third-party scripts: If you use Google Analytics, Facebook Pixel, etc., you must add their domains to script-src and img-src. For example: script-src 'self' https://www.googletagmanager.com.
  • Inline styles: Next.js may generate inline styles (e.g., from styled-jsx). You need 'unsafe-inline' for styles or use a nonce.
  • How OverMCP Can Help

    Manually configuring CSP can be error-prone, especially when you have many dependencies. OverMCP (overmcp.com) automatically scans your Next.js app for missing security headers, including CSP, and provides actionable fixes. It's built for vibe-coded apps to catch security gaps before deployment.

    FAQ

    What is the recommended CSP for a Next.js app?

    The recommended CSP for Next.js includes default-src 'self', script-src 'self' 'nonce-{nonce}' 'strict-dynamic', style-src 'self' 'unsafe-inline', img-src 'self' data: blob:, object-src 'none', base-uri 'self', form-action 'self', and frame-ancestors 'none'. Use a nonce for scripts to avoid 'unsafe-inline'.

    How do I add CSP without breaking Next.js functionality?

    Use middleware to generate a unique nonce per request and add it to both the CSP header and all inline <script> tags. This allows Next.js's own scripts to run without 'unsafe-inline'.

    Can I use CSP with Next.js static export?

    Yes, but you'll need to set the CSP header via the server hosting the static files (e.g., Nginx, Apache, or Vercel). In next.config.js, the headers function works only with Next.js's built-in server. For static export, configure the web server to add the header.

    Is your app secure?

    Free scan in 30 seconds. No signup needed.

    Scan My App Free