Angular 18 SSR项目禁用脚本优化仍遇内联脚本CSP违规
解决Angular 18 SSR项目内联脚本的CSP合规问题
你遇到的内联脚本是Angular SSR客户端hydration(水合)过程中自动注入的引导代码,和angular.json里的脚本优化配置无关——这段代码是框架用来启动客户端与服务端渲染内容同步的核心逻辑,默认会以内联方式执行。以下是彻底解决问题的步骤,无需添加'unsafe-inline':
1. 启用Angular的CSP Nonce支持
Nonce是每个请求唯一的随机字符串,Angular可以利用它标记合法的内联脚本,让CSP策略允许执行这些脚本:
配置angular.json
在项目的build.options中添加CSP配置,指定nonce占位符:
"projects": { "your-project-name": { "architect": { "build": { "options": { // 其他现有配置 "csp": { "enabled": true, "nonce": "{{nonce}}" } } } } } }
修改index.html
在页面头部添加带nonce占位符的CSP meta标签(同时服务端会通过响应头设置更优先级的CSP,这里做兜底):
<head> <!-- 其他meta标签 --> <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'nonce-{{nonce}}'; style-src 'self' 'unsafe-inline';"> </head>
2. 在SSR服务器端生成并注入Nonce
修改你的SSR入口文件(通常是server.ts),生成随机nonce,传递给Angular渲染上下文,并在响应头中设置带nonce的CSP:
import { randomBytes } from 'crypto'; import { NONCE } from '@angular/core'; import { renderApplication } from '@angular/platform-server'; import { AppServerModule } from './src/main.server'; import { readFileSync } from 'fs'; const indexHtml = readFileSync('./dist/your-project/browser/index.html', 'utf-8'); // 处理请求的逻辑中 server.get('*', async (req, res) => { // 生成唯一nonce const nonce = randomBytes(16).toString('base64'); // 渲染应用时注入nonce const html = await renderApplication(AppServerModule, { document: indexHtml.replace('{{nonce}}', nonce), url: req.url, providers: [ { provide: NONCE, useValue: nonce } ] }); // 设置响应头CSP,包含nonce res.setHeader( 'Content-Security-Policy', `script-src 'self' 'nonce-${nonce}'; style-src 'self' 'unsafe-inline';` ); res.send(html); });
3. 可选:禁用Hydration(不推荐)
如果你的项目不需要SSR的交互水合能力,可以直接禁用hydration,彻底消除这段内联脚本:
在app.config.ts中配置:
import { ApplicationConfig, provideClientHydration } from '@angular/core'; export const appConfig: ApplicationConfig = { providers: [ provideClientHydration({ disableHydration: true }) // 其他providers ] };
注意:禁用hydration会让SSR页面失去客户端交互能力,变成纯静态渲染,仅适合纯展示类页面。
为什么之前禁用脚本优化没用?
你看到的内联脚本不属于Angular build阶段的优化产物,而是客户端初始化hydration时框架动态注入的引导代码,optimization.scripts: false只会影响打包后的脚本压缩和内联,对这段框架级的初始化脚本无效。
内容的提问来源于stack exchange,提问作者Ben Reeves
相关产品推荐
相关产品推荐

