Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/config/shared-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,28 @@ Enabling this setting causes vite to determine file identity by the original fil

A nonce value placeholder that will be used when generating script / style tags. Setting this value will also generate a meta tag with nonce value.

::: tip This is a placeholder, not the runtime nonce
`html.cspNonce` must be a deterministic literal placeholder string (for example, `CSP_NONCE_PLACEHOLDER`), not a randomly generated value. The real, per-request random nonce needs to be generated by your HTTP server and substituted into every HTML response just before it is sent, replacing the placeholder in the HTML and added to your `Content-Security-Policy` response header (for example: `script-src 'self' 'nonce-<random>'`).
:::

### When to use (and not use) `html.cspNonce`

Use this option when you have a server that can:

1. Generate a fresh cryptographically strong random nonce on every request.
2. Inject the nonce into the CSP response header.
3. Replace the placeholder value emitted by Vite in the built HTML before sending it to the client.

If you are deploying the built files as immutable static assets (for example on a static host / CDN with `Cache-Control: max-age=31536000, immutable`) and you do not have an HTML edge function or origin server performing on-the-fly substitution, a nonce based policy is usually impractical because the HTML would need to vary per request and therefore cannot be cached immutably. In that scenario prefer a hash-based CSP (hashes of the inline runtime snippets) or a policy that avoids inline code entirely.

### Static hosting & hashes

If you cannot do per-request HTML mutation, consider a hash-based CSP. A community plugin (for example `vite-plugin-csp-guard`) demonstrates a hash workflow. Hash policies allow the HTML to stay byte-for-byte cacheable because the hash values are derived at build time and stay constant as long as the content does.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this plugin work with dynamic imports? #20531 (comment)

Copy link
Contributor Author

@abdelrhman-arnos abdelrhman-arnos Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Hash-based CSP (like vite-plugin-csp-guard) works with dynamic import() as long as your CSP script-src still allows loading the chunk files (e.g. includes 'self' or you use a nonced/hashed entry plus 'strict-dynamic'). A hashes-only policy with neither 'self' nor 'strict-dynamic' will block dynamic imports. A nonce doesn’t conflict with long‑term cached hashed assets, just don’t give the HTML itself an immutable year-long cache or you risk version skew.

Copy link
Member

@sapphi-red sapphi-red Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hash-based CSP (like vite-plugin-csp-guard) works with dynamic import() as long as your CSP script-src still allows loading the chunk files (e.g. includes 'self' or you use a nonced/hashed entry plus 'strict-dynamic').

The reproduction linked in w3c/webappsec-csp#243 (comment) does not work for me even though the code looks fine to me. Is the setup in that repro wrong? or does vite-plugin-csp-guard implement a workaround for that issue?


### Summary

`html.cspNonce` provides a coordination point (placeholder) so Vite can attach the runtime nonce to its injected tags. The security comes only after you implement correct server-side per-request replacement and header emission; using a fixed nonce baked into a static build defeats the protection and should be avoided.

## css.modules

- **Type:**
Expand Down
8 changes: 5 additions & 3 deletions docs/guide/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -768,10 +768,12 @@ To deploy CSP, certain directives or configs must be set due to Vite's internals

When [`html.cspNonce`](/config/shared-options#html-cspnonce) is set, Vite adds a nonce attribute with the specified value to any `<script>` and `<style>` tags, as well as `<link>` tags for stylesheets and module preloading. Additionally, when this option is set, Vite will inject a meta tag (`<meta property="csp-nonce" nonce="PLACEHOLDER" />`).

The nonce value of a meta tag with `property="csp-nonce"` will be used by Vite whenever necessary during both dev and after build.
::: tip Runtime nonce guidance
This value is a placeholder only. Your server must generate a cryptographically strong random nonce per request, place it in the CSP header, and replace the placeholder in the HTML before sending it. See the full guidance, limitations, and static hosting alternatives in the [`html.cspNonce` docs](/config/shared-options#htmlcspnonce).
:::

:::warning
Ensure that you replace the placeholder with a unique value for each request. This is important to prevent bypassing a resource's policy, which can otherwise be easily done.
::: info Static hosting limitations
If you deploy pure static files (e.g. to an object store/CDN with immutable caching) and cannot mutate HTML per request, a nonce-based CSP is usually incompatible because the HTML would need to vary per request to carry the fresh nonce. In such cases use a hash-based CSP (hashes of inline snippets) or remove inline code. Community plugins like `vite-plugin-csp-guard` illustrate a hash approach. See the additional guidance in [`html.cspNonce` docs](/config/shared-options#htmlcspnonce).
:::

### [`data:`](<https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#scheme-source:~:text=schemes%20(not%20recommended).-,data%3A,-Allows%20data%3A>)
Expand Down