nonce必须由服务端生成并同步注入HTML和CSP头,动态创建的script需显式设置nonce属性且值必须与服务端一致;客户端无法继承或读取已有nonce,window.__cspNonce是安全传递的推荐方式。

nonce属性必须由服务端生成并同步注入HTML和script标签
浏览器只认服务端在HTML中写死的nonce值,动态用document.createElement('script')插入的脚本不会自动继承页面已有的nonce。你不能靠JS读取document.querySelector('script[nonce]').nonce再赋给新脚本——这违反CSP设计原则,且多数情况下根本读不到(比如内联脚本没暴露nonce属性)。
正确做法是:服务端生成一次随机nonce,同时用它:
- 渲染HTML根标签或
<meta http-equiv="Content-Security-Policy">中的script-src 'nonce-xxx' - 写入初始
<script nonce="xxx">标签(如有) - 把同一串值作为变量注入到页面JS上下文中(例如
window.__cspNonce = "xxx")
动态创建script时必须显式设置nonce属性
拿到服务端传来的nonce后,创建script元素时得手动赋值,且必须是字符串字面量(不能是表达式或异步获取):
const script = document.createElement('script');
script.nonce = window.__cspNonce; // ✅ 必须是字符串,且和服务端一致
script.src = '/path/to/bundle.js';
document.head.appendChild(script);
注意几个关键点:
立即学习“前端免费学习笔记(深入)”;
-
script.nonce是可写的DOM属性,但设完后不能改;设错或为空会导致加载失败(控制台报CSP错误) - 如果用
innerHTML或insertAdjacentHTML拼接<script nonce="xxx">,需确保xxx是服务端传入、未被XSS污染的原始值 - Webpack/Vite等打包工具若启用了
__webpack_nonce__或__csp_nonce__全局变量,也要确保它和服务端下发的值完全一致
CSP头与HTML中nonce不匹配是最常见的失败原因
即使JS里写了正确的nonce,只要HTTP响应头Content-Security-Policy里的script-src没包含'nonce-xxx'(或用了单引号但值不对),脚本照样被拦截。常见错误包括:
- 服务端模板里拼错了
nonce字符串(多了空格、大小写不一致、用了base64但没解码) - 开发环境开了热更新,CSP头被本地代理覆盖,而HTML里的
nonce还是旧的 - CDN缓存了带旧
nonce的HTML,但CSP头是新的(或反之) - 用了
strict-dynamic却忘了移除nonce——两者可共存,但strict-dynamic本身不豁免nonce校验
无法预知script加载时机时,nonce必须提前注入全局变量
如果你的动态脚本是在用户交互后才加载(比如点击按钮触发),而服务端无法预判这个时机,就不能依赖服务端在响应时写死所有script标签。此时必须把nonce作为纯数据提前塞进页面:
<script> // 在所有业务JS之前执行 window.__cspNonce = "<%= serverGeneratedNonce %>"; </script>
后续任何JS模块都可以安全读取window.__cspNonce。别用data-属性存——dataset可能被框架清理,也容易被误读为HTML实体编码后的值。
真正麻烦的是微前端或多实例场景:每个子应用可能需要独立nonce,但CSP只允许一个script-src策略。这时候得让主应用统一生成并分发,而不是各自为政。



















