---
title: Content Security Policy
description: Allow c15t in a Vue app under a Content Security Policy, with a
  script nonce, the backend in connect-src, and the styles the components
  inject.
group: frameworks
lastModified: "2026-10-10T16:01:45+01:00"
---
## What c15t adds to the page

A Vue app with c15t adds these to the page, and your policy must allow them:

|What|Directive|
|--|--|
|Requests to your backend's `/subjects`, plus `/init` with `hosted()` or `/manifest` with `manifest({ source: 'runtime' })`|`connect-src`|
|Chunks your build splits out, such as the manifest resolver and the copy for a language other than English|`script-src 'self'`|
|`<script>` elements the script loader creates for `scripts`|`script-src`|
|Scripts, frames, images and requests your vendors load|`script-src`, `frame-src`, `img-src`, `connect-src` for each vendor|
|The components' CSS, which Vite bundles with your app|`style-src`|
|The `<style id="c15t-css-vars">` element with your theme tokens, in `<head>`|`style-src`|
|Style bindings, such as the floating trigger's position, set through the CSSOM|None|

## Allow the backend

Add your backend's origin to `connect-src`. With a `manifestURL` or `geoURL`
on another origin, add that origin too. With `iab` set and an IAB TCF policy, the IAB module
fetches the Global Vendor List from the URL the policy gives, usually your
backend.

```txt title="Content-Security-Policy (partial)"
connect-src 'self' https://<your-backend-host>;
```

Use the host from your Inth project or your self-hosted backend.

## Allow scripts

The simplest policy lists each vendor's script host in `script-src`. The
[integration guide](/docs/integrations/overview) for each vendor names its
hosts.

A static Vue app is the same file for every visitor, so it cannot carry a
nonce that changes per response. If your server does generate a nonce per
response, pass it as the plugin's `nonce` option. The script loader adds it
to every `<script>` it creates, and a `nonce` on a single script takes
precedence. Scripts that a vendor script loads itself do not get the nonce.
Add `'strict-dynamic'` to `script-src` to allow them, or list their hosts.

```ts title="src/main.ts"
app.use(c15tVue, { mode: manifest(), nonce, scripts });
```

## Allow styles

The components import their CSS, which Vite adds to your app's stylesheet in
a production build. `style-src 'self'` covers it. In development, Vite injects
`<style>` elements, which need `'unsafe-inline'` in `style-src`.

The c15tVue plugin adds your theme tokens to `<head>` in an inline
`<style id="c15t-css-vars">` element. A `style-src` without
`'unsafe-inline'` blocks it, and the components fall back to the default
theme. The element's content is the same for every visitor and only changes
when you change `tokens`, so you can allow it with a hash. Copy the
`sha256-...` value from the browser's `Refused to apply inline style` console
error into `style-src`, and recompute it whenever you change `tokens`. When
you pass the plugin's `nonce` option, the element carries that nonce too.

Vue applies the components' `style` bindings, such as the floating trigger's
position, through the CSSOM. A policy does not govern CSSOM changes, so they
need no directive.

## Verify

Enforce the policy, not report-only, and load the app. The banner shows your
token colors, and the console shows no
`Refused to connect`, `Refused to load the script` or `Refused to apply inline style` error that mentions your backend, a c15t element or a vendor
you allowed. Click **Accept All**, and the vendor scripts load. The save to
`/subjects` succeeds.
