Когда next dev рендерит, но ничего не работает: проблема с CSP и unsafe-eval

#nextjs#csp#security#frontend

Вот классический сценарий: вы добавляете Content-Security-Policy в Next.js-приложение, проверяете сборку в production — всё работает. Запускаете next dev, страница рендерится, но все клиентские фичи молча умирают. Поиск не фильтрует, кнопки не кликаются, hydration не происходит. При этом в консоли нет явных ошибок, только неприметное сообщение про unsafe-eval. Разберёмся, почему так происходит и как это фиксить.

Почему dev и production ведут себя по-разному

Вот что происходит под капотом:

  1. В development-режиме Next.js использует eval для динамического выполнения кода модулей. Это нужно для React Fast Refresh и HMR — чтобы подменять компоненты без полной перезагрузки страницы.
  2. В production-сборке код компилируется в статические бандлы, и eval не требуется.

Если в CSP нет 'unsafe-eval', браузер блокирует выполнение кода в dev-режиме, но:

// Пример CSP, который сломает next dev
const securityHeaders = [
  {
    key: 'Content-Security-Policy',
    value: "script-src 'self' 'unsafe-inline'", // нет unsafe-eval
  },
]

Два рабочих решения

Вариант A: Динамический CSP для development

Добавляем 'unsafe-eval' только в dev-режиме:

const isDev = process.env.NODE_ENV === 'development'

const csp = [
  "default-src 'self'",
  `script-src 'self' 'unsafe-inline'${isDev ? " 'unsafe-eval'" : ''}`,
  `connect-src 'self'${isDev ? ' ws: wss:' : ''}`,
  // остальные директивы
].join('; ')

// В next.config.mjs
async headers() {
  return [{
    source: '/:path*',
    headers: [{ key: 'Content-Security-Policy', value: csp }],
  }]
}

Важно: оставьте комментарий, почему тут есть условие. Без объяснения это выглядит как security hole, и следующий разработчик может “починить” код, удалив проверку.

Вариант B: Тестировать только на production-сборке

Альтернативный подход — вообще не полагаться на next dev для проверки клиентской логики:

next build && next start

Плюсы:

Минусы:

Этот вариант особенно актуален, если production-среда существенно отличается от dev (например, когда приложение запускается в edge-функциях).

Что ещё может сломаться в dev из-за CSP

'unsafe-eval' — не единственная директива, которая ведёт себя по-разному в dev/prod:

ДирективаЧто нужно в devПочему
script-src'unsafe-eval'Для работы React Refresh
connect-srcws: wss:Для HMR WebSocket
style-src'unsafe-inline'Если dev-сервер инжектит CSS

Как не попадать в эту ловушку

Простой чек-лист для Next.js + CSP:

  1. После добавления CSP запустите next dev
  2. Откройте страницу
  3. Попробуйте кликнуть любую интерактивную кнопку
  4. Проверьте консоль — именно первую ошибку, не последнюю

Это займёт 30 секунд, но покажет разницу между “страница рендерится” и “приложение работает”.

Если используете AI coding tools вроде GitHub Copilot или Cursor, они могут не знать про эту особенность Next.js — код будет выглядеть корректным, но не работать в dev. В таких случаях ручное тестирование критично.

Главный вывод: CSP в Next.js нужно тестировать в обоих режимах, потому что runtime-окружение dev-сервера существенно отличается от production. И да, иногда unsafe-eval — это не баг, а фича.


Попробуй сам: Cursor — AI-редактор для разработчиков.


Источник: https://dev.to/ai_changewatch/next-dev-renders-but-nothing-works-your-csp-is-missing-unsafe-eval-26pl