nonce必须由服务端生成并同步至响应头和script标签,字符级完全一致;Webpack/Vite runtime脚本及动态创建的script也需显式赋值合法nonce。

nonce必须由服务端生成,不能前端拼接
浏览器只认服务端在每次 HTTP 响应时生成的加密安全随机值,Math.random()、Date.now()、硬编码字符串、复用值,全都不行。这些做法会导致 CSP 静默失效——脚本直接不执行,控制台报 Refused to execute inline script,但不会提示“nonce 错了”,只会说策略被违反。
正确做法是用服务端密码学安全随机源:
- Node.js:
crypto.randomBytes(16).toString('base64').replace(/[+/=]/g, '')(必须过滤+、/、=) - Python:
secrets.token_urlsafe(16) - Go:
crypto/rand.Read+ Base64 编码 + 安全字符清洗
生成后必须立刻同步到两个地方:HTTP 响应头和 HTML 的 <script> 标签,缺一不可。
响应头和 script 标签的 nonce 值必须逐字节一致
浏览器校验极其严格,任何微小差异都会导致匹配失败。不是“差不多就行”,而是要求字符级完全相等。
立即学习“前端免费学习笔记(深入)”;
常见不一致场景:
- 响应头写成
script-src 'nonce-abc123 '(末尾空格),HTML 里是nonce="abc123" - 大小写混用:
nonce="Abc123"vs'nonce-abc123' - 模板插值时未做 HTML 安全转义,nonce 值含
"或&导致标签提前闭合 - 把 nonce 写进
<meta http-equiv="Content-Security-Policy">—— 这种方式完全不支持 nonce,只响应头有效
验证方法:打开 DevTools → Network → 找对应请求 → Headers 查看 Content-Security-Policy 值;再右键页面 → “查看页面源代码”,搜索 <script nonce=,手动比对两处字符串是否完全相同(建议复制粘贴比对)。
Webpack/Vite 的 runtime 脚本也要带 nonce
你手动写的 <script nonce="xxx"> 加上了,但 Webpack 注入的 __webpack_require__ 启动逻辑、Vite 的 HMR 初始化脚本、React/Vue 的 hydration runtime,默认都不带 nonce。它们同样是内联脚本,同样会被 CSP 拦截,结果就是白屏或功能异常。
解决路径分两步,缺一不可:
- 告诉打包工具当前请求的 nonce 值:
– Webpack:在入口 JS 执行前设置全局变量__webpack_nonce__ = 'xxx'
– Vite:需在 HTML 模板中显式注入,或使用vite-plugin-csp类插件传入 - 确保最终输出的 HTML 中,那些自动注入的
<script>标签也真实含有nonce="xxx"属性 —— 仅设 JS 变量不够,必须渲染进 DOM 标签
注意:__webpack_nonce__ 只是提示 Webpack “该用哪个 nonce”,但若响应头没包含 'nonce-xxx',它依然无效。
动态创建的 script 标签必须手动赋值 nonce
如果你在运行时用 document.createElement('script') 插入脚本,哪怕 CSP 已启用 'strict-dynamic',这个新创建的 <script> 仍需显式携带匹配的 nonce 属性,否则不执行。
正确写法是:
const script = document.createElement('script');
script.nonce = nonce; // ✅ 推荐:直接赋值属性
// script.setAttribute('nonce', nonce); // ❌ 部分浏览器不识别
script.textContent = 'console.log("allowed");';
document.head.appendChild(script);
其中 nonce 必须从服务端注入的来源读取(如 <meta name="csp-nonce" content="xxx">),不能硬编码,也不能从 URL 参数或 localStorage 读 —— 这些都不可信。
容易被忽略的一点:如果脚本是通过 innerHTML 插入的,比如 el.innerHTML = '<script>...</script>',这种写法无法自动继承 nonce,且现代浏览器会忽略其中的 nonce 属性。必须用 createElement + nonce 属性赋值的方式。



















