服务端生成的nonce必须在html解析前注入script标签,动态js补nonce无效;需确保服务端模板、csp响应头、webpack/vite构建产物、第三方sdk初始化四者nonce值字符级完全一致,并配合'strict-dynamic'启用动态脚本执行。

服务端生成的 nonce 必须在 HTML 解析前注入 script 标签
浏览器在解析 HTML 文档流时就完成 nonce 校验,JS 执行阶段早已错过时机。任何用 document.getElementById('boot').setAttribute('nonce', 'abc123') 这类动态补属性的操作,完全无效——脚本已被跳过,校验早已结束。
正确路径只有一条:服务端模板渲染阶段,把本次请求生成的 nonce 值直接插进 <script></script> 标签。例如:
- Express + EJS:
<script nonce="<%= nonce %>">window.config = { ... };</script> - FastAPI + Jinja2:
<script nonce="{{ nonce|safe }}"></script>(|safe不可省,否则引号、等号会被转义) - Next.js App Router:需在
generateMetadata或自定义html模板中透出,不能依赖客户端 hydration 补充
检查是否生效,必须右键「查看网页源代码」,搜索 <script nonce="</code">,确认值存在且未被截断;同时用 DevTools Network 面板看响应头中 <code>Content-Security-Policy</script> 的 'nonce-xxx' 是否与之字符级一致(大小写、空格、末尾换行都不能有差异)。
Webpack/Vite 构建产物里的内联 runtime 脚本也要带 nonce
你手动写的 <script nonce="xxx"></script> 能过,不代表页面能正常运行——Webpack 的 __webpack_require__ 启动逻辑、Vite 的 HMR 初始化代码、甚至 import() 动态加载的 chunk wrapper,默认都是无 nonce 的内联脚本,CSP 会直接拦截,导致白屏或功能异常。
修复方式因工具而异:
- Webpack:在入口 JS 文件最顶部(早于任何模块执行)声明
window.__webpack_nonce__ = nonceValue,Webpack 会自动将该值注入所有生成的内联 runtime - Vite:启用
build.rollupOptions.output.manualChunks并配合html.cspNonce配置(Vite ≥ 5.0),确保所有构建时注入的<script></script>和<style></style>标签都携带 nonce - 切勿在构建时硬编码 nonce 值(如
index.html静态模板里写死nonce="abc"),这会让整个机制失效——nonce 必须随每次 HTTP 响应动态生成
CSP 响应头必须同时含 'nonce-xxx' 和 'strict-dynamic'
只配 script-src 'nonce-abc123',页面里用 document.createElement('script') 加载后续逻辑,照样被拦。因为 nonce 只授权初始 HTML 中已存在的内联脚本,不授权 JS 动态创建的脚本。
要让动态脚本合法执行,必须开启 'strict-dynamic',但有强约束:
- 响应头必须写成:
Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic'(顺序无关,但二者必须共存) -
'self'或'unsafe-inline'与'strict-dynamic'共存时,前者会覆盖后者效果,导致 strict-dynamic 失效 - 首个执行的
<script nonce="abc123"></script>必须真实包含初始化逻辑(不能是空标签或仅注释),否则浏览器拒绝激活 strict-dynamic 链路 - 后续动态创建的 script,必须显式设置
script.nonce = 'abc123',值必须与初始 nonce 完全一致
第三方 SDK 动态挂载的 script 默认不带 nonce
客服弹窗、AI 浮窗等第三方 SDK,通常通过 document.createElement('script') 插入资源或执行初始化代码。它们自身不读取页面已有 script.nonce,也不感知 CSP 策略——你无法靠 JS “补”上去,因为校验早已完成。
真正有效的只有两种方式:
- SDK 的初始化脚本本身由服务端渲染进 HTML,并显式携带 nonce:
<script nonce="abc123">__wxSdkInit({ ... });</script> - SDK 的 runtime 脚本由服务端模板控制输出,即:HTML 模板中手动注入带 nonce 的
<script src="/sdk-init.js" nonce="abc123"></script>,而非交由 SDK 自己动态创建 - 微信公众号等环境更敏感:CDN 缓存可能导致 nonce 复用;Nginx 或中间件可能覆盖或篡改 CSP 响应头;务必检查
curl -I输出,确认 header 未被污染
最容易被忽略的是:nonce 值一旦生成,就必须贯穿服务端模板、HTTP 响应头、构建工具 runtime 注入、第三方 SDK 初始化四个环节,缺一不可。任一环节脱节,都会静默失败——没有报错,只有脚本不执行。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











