DEV Community

Jen C.
Jen C.

Posted on

Security - Solving the "Content Security Policy (CSP) Header Not Set" in Next.js: script-src and connect-src

Resources

@next/react-refresh-utils

What is React Fast Refresh?

Content-Security-Policy: script-src directive

Content-Security-Policy: connect-src directive

Background

Referencing the previous post, we have a basic setup for the Content Security Policy (CSP) headers with the script-src directive in the src/middleware.js file of the project.

Errors and solutions

Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'nonce-xxx'

Error message:

Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'nonce-YjQ3MGE2NDEtYTE2ZS00ODU5LTgzYjEtY2I0ODQ0OTQ3YWJi' https://my.domain.com 'strict-dynamic'".

    at ./node_modules/next/dist/compiled/@next/react-refresh-utils/dist/runtime.js (react-refresh.js:30:26)

    ...
Enter fullscreen mode Exit fullscreen mode

Root cause

Because the module used for React Refresh during development contains code that uses eval(), the current CSP script-src directive is not sufficient, as it does not include the 'unsafe-eval' source expression. As shown below:

script-src 'self' 'nonce-${nonce}' ${authUrl} 'strict-dynamic';
Enter fullscreen mode Exit fullscreen mode

Solution

For security reasons, we should not include 'unsafe-eval' in the production environment. Therefore, we should check the current environment and add 'unsafe-eval' only when it is not a production environment.

const authUrl = `${myAuthURL.protocol}//${myAuthURL.host}`;

const isProd = process.env.NODE_ENV === 'production';

export function middleware(request) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
  const cspHeader = `
    default-src 'self';
    script-src 'self' ${isProd ? '' : "'unsafe-eval'"} 'nonce-${nonce}' ${authUrl} 'strict-dynamic';
`;
Enter fullscreen mode Exit fullscreen mode

Refused to connect to '<URL>' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback

Error message:

Refused to connect to '<URL>' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.

Refused to connect to 'https://sr-client-cfg.amplitude.com/config?api_key=8750e93cf3cf432b04d4644133f3aa5c&config_keys=analyticsSDK&session_id=1750047771451' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.

...
Enter fullscreen mode Exit fullscreen mode

Root cause

'connect-src' directive is not set.

Solution

Set the 'connect-src' directive in the Content Security Policy (CSP) headers, and start with a basic value:

connect-src 'self';
Enter fullscreen mode Exit fullscreen mode

Then, inspect the project codebase and monitor error messages in Chrome DevTools to update the connect-src values accordingly. For example, if the error message is:

Refused to connect to '<URL>' because it violates the following Content Security Policy directive: "connect-src 'self'".

Refused to connect to 'https://api2.amplitude.com/2/httpapi' because it violates the following Content Security Policy directive: "connect-src 'self'".

...
Enter fullscreen mode Exit fullscreen mode

As shown in the image:

Image description

We should add the URL https://api2.amplitude.com/2/httpapi to the connect-src directive:

connect-src 'self' https://api2.amplitude.com/2/httpapi;
Enter fullscreen mode Exit fullscreen mode

Additionally, for URLs with dynamic query parameters (e.g. https://sr-client-cfg.amplitude.com/config?api_key=...&session_id=...), extract only the protocol and host, then add it to connect-src. The updated directive would look like:

connect-src 'self' https://api2.amplitude.com/2/httpapi https://sr-client-cfg.amplitude.com;
Enter fullscreen mode Exit fullscreen mode

According to Content-Security-Policy: connect-src directive

The following APIs are controlled by this directive:

  • The ping attribute in <a> elements
  • fetch()
  • fetchLater() Experimental
  • XMLHttpRequest
  • WebSocket
  • EventSource
  • Navigator.sendBeacon()

To ensure compliance, review the codebase for any usage of these APIs that make external network requests. If any requests are made to URLs not currently listed in the connect-src directive, update the CSP header to include those origins accordingly.

Top comments (0)