You need to enable JavaScript to run this app. Next.js配置严格CSP导致预加载脚本被拦截如何解决
优惠活动
大模型
产品
解决方案
定价
更多

Next.js配置严格CSP导致预加载脚本被拦截如何解决

严格CSP下Next.js预加载chunk被拦截的修复方案

拦截原因

  • Next.js自动生成的<link rel="preload" as="script">标签默认需要匹配配置的nonce才能通过CSP校验,但原配置中nonce值是在组件render方法中生成的,而Next.js的内联脚本源码获取、预加载标签生成逻辑都是在getInitialProps阶段执行的,两个阶段的上下文独立,生成的nonce值不匹配,导致预加载标签携带的nonce和CSP规则里声明的nonce对不上,直接触发拦截。
  • 原先通过<meta httpEquiv="Content-Security-Policy">传递CSP规则的方式存在兼容缺陷:浏览器对meta形式的CSP不会完全启用strict-dynamic的全量校验逻辑,同时在script-src中追加的https: http: 'unsafe-inline' 'unsafe-eval'配置,既会破坏严格CSP的安全边界,也无法让预加载标签通过校验——存在strict-dynamic时,普通scheme类的源规则会被浏览器自动忽略。

修复步骤

  • 移除CSP规则中多余的安全冗余配置:

    • script-src字段删除'unsafe-inline'、'unsafe-eval'、https:、http:这几个值。严格CSP模式下,nonce存在时unsafe-inline会被浏览器自动忽略,全scheme通配和eval授权会直接抵消严格CSP的防护效果。
  • 调整nonce的生成时机:

    • 将nonce的生成逻辑从render方法移到getInitialProps阶段,保证和Next.js的内联脚本计算、预加载标签生成在同一个上下文执行,确保两边使用的nonce值完全一致。
  • 调整CSP的传递方式:

    • 放弃在<Head>中插入meta标签传递CSP的写法,改为在服务端渲染阶段通过服务端响应头设置CSP规则。响应头形式的CSP规则校验逻辑完全符合W3C标准,不会出现meta形式下预加载资源校验失效的问题。
  • 给所有Next.js注入的资源标签匹配nonce:

    • 将同一上下文中生成的nonce从getInitialProps阶段传递到组件props,给<NextScript>组件传入nonce值,服务端渲染阶段会自动给所有生成的预加载标签、脚本标签补充对应的nonce属性。

    修正后的_document.js核心参考代码:

    import Document, { Html, Head, Main, NextScript } from 'next/document'
    import crypto from 'crypto'
    
    const cspHashOf = (text) => {
      const hash = crypto.createHash('sha256')
      hash.update(text)
      return `'sha256-${hash.digest('base64')}'`
    }
    
    export default class MyDocument extends Document {
      static async getInitialProps(ctx) {
        // 同一上下文生成nonce值,保证和Next.js资源生成逻辑使用同一份值
        const nonce = crypto.randomBytes(16).toString('base64')
        // 定义严格CSP规则
        const cspConfig = {
          'object-src': "'none'",
          'base-uri': "'self'",
          'script-src': `'nonce-${nonce}' 'strict-dynamic' ${cspHashOf(
            NextScript.getInlineScriptSource(ctx)
          )}`,
        }
        // 服务端设置CSP响应头
        const cspStr = Object.entries(cspConfig).map(([key, value]) => `${key} ${value}`).join('; ')
        if (ctx.res) {
          ctx.res.setHeader('Content-Security-Policy', cspStr)
        }
        const initialProps = await Document.getInitialProps(ctx)
        // 把nonce传到组件渲染props
        return { ...initialProps, nonce }
      }
    
      render() {
        const { nonce } = this.props
        return (
          <Html>
            <Head />
            <body>
              <Main />
              {/* 传入nonce给NextScript,自动给所有生成的脚本、预加载标签注入对应nonce */}
              <NextScript nonce={nonce} />
            </body>
          </Html>
        )
      }
    }
    
  • 特殊场景适配:

    • 如果开发环境需要使用eval能力(比如热更新调试),可以在开发环境下动态给script-src追加'unsafe-eval'值,生产环境绝对不要保留该配置。

注意:配置完成后需要重启Next.js服务,清理之前的构建缓存,否则旧的不带nonce的预加载标签可能仍然会被拦截。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 02:36:08