---
title: Content Security Policy
description: Allow c15t in a Nuxt app under a Content Security Policy, including
  the theme token style tag, same-origin routes, the backend and vendor scripts.
group: frameworks
lastModified: "2026-10-10T16:01:45+01:00"
---
## What c15t adds to the page

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

|What|Directive|
|--|--|
|The `<style id="c15t-css-vars">` tag with your theme tokens, in the page head|`style-src`|
|The components' CSS: inline `<style>` tags Nuxt writes for the server-rendered banner, and stylesheets Nuxt bundles with your app|`style-src`|
|On `ssr: false` pages in `hosted()` mode, an inline `<script>` in the page head that starts `/init`|`script-src`|
|On server-rendered pages with a banner, an inline `<script>` before its buttons that holds a tap made before hydration|`script-src`|
|Browser requests to `/api/c15t/init` and `/api/c15t/manifest` in the default `manifest()` mode|`connect-src 'self'`|
|Consent saves to your backend's `/subjects`, plus `/init` in `hosted()` mode|`connect-src`|
|Chunks Nuxt splits out, such as the browser resolver with `manifest({ resolve: 'browser' })` 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|

Nuxt adds its own inline scripts and, by default, inline styles. Its
documentation and modules such as `nuxt-security` cover those. The banner's
first paint uses Nuxt's inline styles. If your policy cannot allow them, turn
them off with `features: { inlineStyles: false }` in `nuxt.config.ts`: Nuxt
then links the banner's CSS as stylesheets, and the module leaves those links
in place.

## Allow the theme token style tag

The module writes the `tokens` option into an inline
`<style id="c15t-css-vars">` tag on every page. A `style-src` without
`'unsafe-inline'` blocks it, and the banner falls back to the default theme. The tag's content
is the same for every visitor and only changes when you change `tokens`, so
you can allow it with a hash:

1. Load a page with the policy enforced.
2. Copy the `sha256-...` value from the browser's `Refused to apply inline style` console error.
3. Add it to `style-src` and redeploy.

Recompute the hash whenever you change `tokens`. When you set the module's
`nonce` option, the tag carries that nonce, so a `style-src` that lists the
same nonce allows it. When your policy already allows `'unsafe-inline'` for
Nuxt's own styles, the tag needs nothing more.

## Allow the early init script

On a page with `ssr: false` in `hosted()` mode, the module writes an inline
`<script>` into the page head that requests your backend's `/init` before the
app's JavaScript loads. A `script-src` without `'unsafe-inline'` blocks it, and
the app then sends its own `/init` request after it loads, as it would without
the script.

* **With `nuxt-security`**, the script carries its per-request nonce. Nothing
  more is needed.
* **With the module's `nonce` option**, the script carries that nonce.
* **With a hash-based policy**, the script's content is the same for every
  visitor and changes only when you change `backendURL` or upgrade c15t. Copy
  the `sha256-...` value from the browser's `Refused to execute inline script`
  console error and add it to `script-src`.

To leave the script out, set `initPrefetch: false` in the module options.

A server-rendered banner also writes an inline `<script>` before its buttons.
It holds a tap the visitor makes before the app hydrates. It carries the
module's `nonce` option when set, and its content is the same for every
visitor, so a hash works too. If your policy blocks it, the banner still works
after hydration, but a tap before then does nothing.

## Allow the backend

In the default `manifest()` mode the browser calls your own origin for
`/api/c15t/init`, which `'self'` covers, and sends consent saves to your
backend. Add the backend's origin to `connect-src`. A `manifestURL` or
`geoURL` on another origin needs that origin too. With `iab` set and an IAB
TCF policy, the browser loads the Global Vendor List through the module's
consent route in `manifest()` mode, or from your backend otherwise.

```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. Requests the
Nuxt server makes during rendering are not subject to the browser's policy.

## Allow scripts

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

The module's `nonce` option adds a nonce to every script the loader creates.
Module options and `app.config.ts` are fixed when the app builds, so every
response gets the same value. A nonce only protects a page when it changes on
each response, so prefer the host allowlist for c15t's scripts.

## Verify

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