Under a nonce-based CSP, why do an Angular app's component styles need a nonce, and when do you use CSP_NONCE versus the ngCspNonce attribute?
answer
- styles inserted at runtime
- style-src without unsafe-inline
- root element attribute
- injection token read by renderers
- cacheable index.html
basics
~20 sAngular inserts component styles as <style> elements at runtime, which a strict style-src blocks unless they carry the nonce. Use ngCspNonce when the server templates index.html per response, and CSP_NONCE when the nonce arrives at runtime and index.html stays cacheable.
solid answer
~40 sComponent styles are not in your stylesheet file: Angular's renderer adds them to the document as `<style>` elements when a component is first rendered, and with `ShadowDom` encapsulation it adds them inside each shadow root. A strict `style-src 'self' 'nonce-…'` rejects those inline styles unless each one carries the page's nonce, so Angular needs to know it. You give it the nonce in one of two runtime ways. `ngCspNonce="…"` on the root element suits servers that template `index.html` per response and put the same value in the header. The `CSP_NONCE` injection token from `@angular/core` suits apps that learn the nonce at runtime and want to cache `index.html`. The token's default factory simply reads the `ngCspNonce` attribute, so the two are the same channel. The guide's third route is the CLI's `autoCsp` option.
code
html · 4 lines<!-- index.html rendered per response; the same value is in the CSP header -->
<body>
<app-root ngCspNonce="q3Kx9vT2mB7wLr0p"></app-root>
</body>go deeper
Recall that Angular adds component styles to the page at runtime, so a strict style-src blocks them unless Angular knows the nonce.
Explain the two channels, ngCspNonce on the root element and the CSP_NONCE token, and that the token's default reads the attribute.
Choose the channel from how index.html is served, handle the critical-CSS exception, and note that JSONP, SSR event replay and ShadowDom styles use the same token.
Decide where nonce generation lives in the serving stack and whether the team accepts 'unsafe-inline' for styles or pays for per-response HTML.
## The problem: styles Angular creates at runtime A **Content Security Policy (CSP)** is a response header that tells the browser which scripts and styles a page may use. A strict policy drops `'unsafe-inline'` and allows inline code only if it carries a **nonce**, a random value generated for each response and repeated in the header. Angular components declare styles in `styles` or `styleUrl`. Those styles are compiled into the JavaScript bundle and, at runtime, the renderer inserts them into the document: - for the default emulated encapsulation and for `None`, as `<style>` elements in the document head, added once per component type; - for `ShadowDom` encapsulation, as `<style>` elements inside each component's shadow root. To a CSP these are **inline styles**. Under `style-src 'self' 'nonce-abc'` the browser refuses every one that lacks `nonce="abc"`, and the app renders unstyled while the console fills with violations. Angular can add the attribute itself, but only if it knows the value. ## Three ways to hand Angular the nonce The security guide lists three options: | Option | Where the value comes from | Fits when | | :--- | :--- | :--- | | `ngCspNonce` attribute on the root element | server-side templating writes it into `index.html` | the server renders `index.html` per response and can put the same value in the header | | `CSP_NONCE` injection token | any runtime source you provide it from | the nonce is available at runtime and `index.html` should stay cacheable | | `autoCsp` workspace option | the CLI build | you want the CLI to handle the policy for the bootstrap scripts (a build-system subject) | The first two are closer than they look. `CSP_NONCE` is an `InjectionToken<string | null>` whose **default factory** searches the document body for an element with an `ngCspNonce` attribute and returns its value, or `null`. So: 1. If you provide `CSP_NONCE` yourself, your value wins. 2. If you do not, Angular falls back to the `ngCspNonce` attribute. 3. If neither exists, styles are inserted without a nonce. The factory reads the DOM because the value must be ready **very early**, when renderers are created during bootstrap, before any component exists. ## Who else reads the token `CSP_NONCE` is not only for component styles. In Angular 22.2 it is also read by: - the shared styles host and the `ShadowDom` renderer, for `<style>` and external stylesheet `<link>` elements; - `HttpClient`'s JSONP backend, which adds the nonce to the `<script>` it injects; - server-side rendering, which puts it on the small inline script that replays events captured before hydration; - signal forms' input-validity monitor, which inserts a small `<style>` element. So the token is the single place to feed a per-response nonce into everything Angular injects. ## The critical-CSS exception The guide adds a caveat: if the build **inlines critical CSS** into `index.html`, you cannot use `CSP_NONCE`, and should use `ngCspNonce` or `autoCsp` instead. The inlined styles and their loader live in the HTML file itself, outside Angular's runtime, so a value that exists only in the injector cannot reach them. That is also the one case where the guide's minimal policy needs a nonce in `script-src`. ## Common mistakes - **Setting the header but not telling Angular.** The policy is correct, yet every component renders unstyled, because nothing supplied the value to `CSP_NONCE` or `ngCspNonce`. - **Putting the nonce on the wrong element.** The default factory looks for an element carrying `ngCspNonce` inside `document.body`; the guide's pattern puts it on the root component element, such as `<app-root ngCspNonce="…">`. - **Providing the token too late.** Renderers read the value during bootstrap, so the provider belongs in the application's bootstrap providers, not in a lazily loaded route. - **Assuming the nonce covers everything.** Global stylesheets loaded from the origin are allowed by `'self'`; the nonce is for the elements Angular inserts at runtime, and third-party code that injects its own styles needs its own handling. ## What stays your job Angular only **applies** the nonce. Generating a fresh, unguessable value for every response and writing the same value into both the header and the page is the serving layer's job. A nonce frozen into a cached `index.html` gives no protection, and threading it through a CDN is a CSP-deployment subject rather than an Angular one. If you cannot generate nonces at all, the guide's fallback is `'unsafe-inline'` in `style-src`, which gives up protection against injected styles.
- If you provide neither CSP_NONCE nor ngCspNonce, what does Angular do with component styles?It inserts them without a nonce attribute. The token's default factory returns `null` when no element carries `ngCspNonce`, and the renderer only sets the attribute when a value exists. Under a policy without `'unsafe-inline'` for styles, the browser then blocks every component style.
- Why can't CSP_NONCE cover critical CSS inlined into index.html?Critical-CSS inlining writes styles into the HTML file itself during the build or server render, before Angular's injector exists. A value provided through the token only reaches code running inside the app, so the inlined styles need the nonce in the HTML: the guide recommends `ngCspNonce` or `autoCsp` for that case.
saying these in an interview costs you the question
- Component styles come from the global stylesheet, so CSP never affects them.
- CSP_NONCE and ngCspNonce are unrelated mechanisms that must both be set.
- Angular generates a fresh nonce itself on every page load.
- A nonce baked into a cached index.html still protects the app.
- CSP_NONCE works for critical CSS inlined into index.html.