The Symptom: A Silent Failure

You run next dev. The application builds and renders perfectly. The layout is correct, the styles are applied, and the server-fetched data appears as expected. Yet, nothing interactive works. Search boxes don't filter, sort headers fail to sort, and buttons like "show more" are unresponsive. Crucially, there are no visible error messages, no red overlay indicating a failure, and the browser's Network tab shows no failed requests. This frustrating scenario, where a Next.js application appears functional but lacks all client-side interactivity, is often caused by an overly restrictive Content Security Policy (CSP).

The root cause lies in how Next.js's development server operates. During development, Next.js utilizes tools like Webpack for hot module replacement and other dynamic features that rely on executing inline scripts and `eval()` to function correctly. These operations, however, are flagged as potentially insecure by browsers if they are not explicitly permitted by the Content Security Policy. When a CSP is in place that forbids `script-src` directives from including `'unsafe-eval'` or `'unsafe-inline'`, the browser will block these critical JavaScript executions, leading to the observed failure of all interactive elements.

The author of AI Change Watch, a project that monitors AI vendor publications, encountered this precise issue after implementing a CSP on their production site. While the production environment remained unaffected (likely due to different server configurations or static asset handling), the local development server broke entirely. This highlights a common pitfall: a CSP that is perfectly valid for a static production build can inadvertently cripple a dynamic development environment.

Understanding Content Security Policy (CSP)

Content Security Policy (CSP) is a security standard that helps mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. It works by specifying which dynamic resources (scripts, styles, images, etc.) are allowed to be loaded and executed by a browser. A CSP is typically delivered via an HTTP header or a meta tag. For example, a basic CSP might look like this:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; object-src 'none';

This policy dictates that by default, resources should only be loaded from the same origin (`'self'`). It also explicitly allows scripts from `'self'` and `https://cdn.example.com`, while disallowing all `object-src` (like Flash or Java applets).

The `unsafe-eval` and `unsafe-inline` Directives

The problem arises with two specific CSP directives: `'unsafe-eval'` and `'unsafe-inline'`.

  • 'unsafe-eval': This directive allows the use of JavaScript functions like `eval()`, `setTimeout()`, and `setInterval()` when they are passed a string that is not a function (e.g., eval("some string")). Many JavaScript bundlers and development tools, including Webpack used by Next.js, rely on dynamic code generation and evaluation, which is often blocked if `'unsafe-eval'` is not permitted.
  • 'unsafe-inline': This directive allows the execution of inline JavaScript code, such as inline `