You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js内部代码块触发CSP违规,如何无需unsafe-inline修复?

Next.js App Router配置Nonce后仍触发内联样式CSP错误的原因及解决办法

问题场景

控制台抛出CSP错误:

52774a7f-5ebbf62ba1149ac0.js:1 Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' 'nonce-bO6ZusgtyACaFFRtXb+Rng==' https://fonts.googleapis.com".

当前配置:

  1. 通过Middleware生成Nonce并注入CSP头:
export async function middleware(request: NextRequest) {
    const { csp, nonce } = generateCsp();
    const requestHeaders = new Headers(request.headers);
    requestHeaders.set("x-nonce", nonce);
    const headerKey = "content-security-policy";
    requestHeaders.set(headerKey, csp);
    
    const response = NextResponse.next({
        request: { headers: requestHeaders },
    });
    response.headers.set(headerKey, csp);
    return response;
}
  1. CSP的style-src已包含'nonce-${nonce}',且在app/layout.js中给<head>添加了nonce属性:
<html lang="en">
      <head nonce={nonce}>
        <link rel="icon" href="/cta_desktop-min.png" />
        <Script id="nonce-script" strategy="beforeInteractive" nonce={nonce}>
          {`window.__nonce = "${nonce}";`}
        </Script>
        <GoogleAnalytics gaId="G-4EG6E9NGEM" />
      </head>
      <body>
        <Providers nonce={nonce}>
          <Toaster />
          <NextTopLoader />
          {children}
        </Providers>
      </body>
    </html>

原因分析

  • Next.js App Router在SSR/SSG过程中,部分内部动态生成的内联样式(比如组件 hydration 时插入的样式)不会自动继承你给<head>设置的nonce,导致这些样式无法通过CSP校验。
  • 仅给<head>添加nonce无法覆盖框架所有的内联样式注入场景,尤其是Next.js自身的样式系统生成的内联标签。

解决方法(无需使用unsafe-inline)

1. 传递Nonce到Next.js样式注册器

利用next/navigation的useServerInsertedHTML钩子,手动给Next.js生成的内联样式标签添加nonce:

// app/layout.js
import { useServerInsertedHTML } from 'next/navigation';
import { useState } from 'react';
import Script from 'next/script';

export default function RootLayout({ children, nonce }) {
  const [pendingStyles, setPendingStyles] = useState([]);

  useServerInsertedHTML(() => {
    const stylesToRender = pendingStyles.map(style => (
      <style
        key={style.id}
        nonce={nonce}
        dangerouslySetInnerHTML={{ __html: style.css }}
      />
    ));
    setPendingStyles([]);
    return <>{stylesToRender}</>;
  });

  // 自定义样式收集函数,供组件传递样式
  const addStyle = (style) => {
    setPendingStyles(prev => [...prev, style]);
  };

  return (
    <html lang="en" nonce={nonce}>
      <head nonce={nonce}>
        <link rel="icon" href="/cta_desktop-min.png" />
        <Script id="nonce-script" strategy="beforeInteractive" nonce={nonce}>
          {`window.__nonce = "${nonce}";`}
        </Script>
        <GoogleAnalytics gaId="G-4EG6E9NGEM" />
      </head>
      <body>
        <Providers nonce={nonce} addStyle={addStyle}>
          <Toaster />
          <NextTopLoader />
          {children}
        </Providers>
      </body>
    </html>
  );
}

2. 补充内联样式哈希到CSP

如果上述方法无效,可从控制台错误中提取被拦截内联样式的哈希值,添加到style-src规则中:

  • 控制台错误会显示类似Hashes: sha256-xxxxxx的内容,将'sha256-xxxxxx'追加到style-src的CSP字符串里。
  • 注意:哈希值与具体内联样式绑定,Next.js版本更新或内部逻辑变更后可能需要重新获取。

3. 校验Nonce生成与CSP头的正确性

  • 确保generateCsp函数正确生成包含nonce的CSP规则,示例:
import crypto from 'crypto';

function generateCsp() {
  const nonce = crypto.randomBytes(16).toString('base64'); // 安全的随机生成方式
  const csp = `
    default-src 'self';
    style-src 'self' 'nonce-${nonce}' https://fonts.googleapis.com;
    // 其他CSP规则...
  `.replace(/\n/g, ' ');
  return { csp, nonce };
}
  • 确认Middleware中同时在请求头和响应头正确设置了CSP,避免Next.js在处理请求时无法读取到nonce。

内容的提问来源于stack exchange,提问作者Kahkasha Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 18:43:11