SvelteKit Advanced
Content Security Policy
Configure csp
SvelteKit writes the Content-Security-Policy header when you set its csp
option. In SvelteKit 3 that option goes to the sveltekit() plugin in
vite.config.ts. SvelteKit adds nonces or hashes to its own inline scripts.
Add the hosts c15t and your vendors need to the directives:
On SvelteKit 2, set the same csp object as kit.csp in svelte.config.js.
Replace https://your-project.inth.app with the backend URL from your Inth
project, and list each vendor's script host.
SvelteKit does not pass the nonce it generates to your components, so the
provider's nonce prop cannot reuse it. Allow vendor hosts in script-src
instead. Host sources still apply next to SvelteKit's nonces, as long as the
list has no 'strict-dynamic'.
The <link rel="modulepreload"> tag that c15tHandle adds for the script
loader and network blocker chunk takes the nonce SvelteKit put on its own
scripts, unless the provider has a nonce of its own. The chunk is on your
own origin, so 'self' covers it too.
Allow c15t's style elements
c15t's components add their rules as <style> elements. On a server-rendered
page, c15tHandle writes the banner's rules into the HTML <head> with the
provider's nonce, or else the nonce SvelteKit put on its own scripts, so a
nonce-based policy admits them. A prerendered page under mode: 'auto' gets
hashes instead of nonces, so there is no nonce for c15tHandle to copy.
The standard and IAB dialogs and other components that first render in the
browser use the provider's nonce, or inherit the nonce from an existing
c15t style or a page script. Your style-src must allow that nonce.
For a prerendered page, or when your policy does not allow those nonces, set
styles={false} on ConsentRoot and import
@c15t/svelte/styles.css in your root layout. With IAB TCF, also import
@c15t/svelte/iab/styles.css after it. 'self' covers those stylesheets.
Allow the generated theme
The <style id="c15t-theme"> element from
customize is inline CSS that
you render with {@html}. SvelteKit's csp option adds no nonce or hash to it, so a
style-src without 'unsafe-inline' blocks it and the banner falls back to
the stock colors. Either add the SHA-256 hash of the element's text to
style-src, which stays fixed while the theme does not change, or switch to
a stylesheet, which 'self' covers.
Browsers ignore 'unsafe-inline' in a list that also has a nonce or hash, so
adding it does not help under mode: 'nonce' or 'auto'.
Style attributes in server HTML
In the browser, Svelte sets style values through the DOM, which a policy
does not block. Server-rendered HTML is different. A style attribute in the
HTML is inline style, and style-src without 'unsafe-inline' blocks it.
c15t renders style attributes for theme slot style values and for the
position of ConsentDialogTrigger. Svelte does not set them again during
hydration, so a blocked value stays unapplied.
Use class names in theme slots instead of style objects, or allow style
attributes with a separate style-src-attr 'unsafe-inline' directive.
Server requests
loadConsent, c15tHandle and the manifest route call the backend from your
server, which the browser's policy does not govern. Only the browser's own
requests, such as saving a choice, need the backend in connect-src. With
the consent proxy, those
go to your own origin and 'self' covers them.
What c15t adds to the page
A Content Security Policy has to allow each thing c15t or your vendors put on the page:
| c15t does | Directive | Allow |
|---|---|---|
Requests the policy from /init and saves choices to /subjects | connect-src | Your backend's origin. 'self' covers a same-origin route such as /api/c15t. |
Adds a <script> element for each allowed entry in scripts | script-src | Each vendor's script host, or the provider's nonce. |
| Lets vendor scripts load more scripts, pixels and requests | script-src, img-src, connect-src | The hosts each vendor documents. |
Sets src on gated iframes, or mounts ConsentGate children | frame-src | Each embed's host, such as https://www.youtube-nocookie.com. |
Adds its rules to <head> as <style> elements | style-src | The provider's nonce. With styles={false} and an imported @c15t/svelte/styles.css, 'self'. |
| Fetches the Global Vendor List under an IAB policy | connect-src | The vendor list host the policy names, when it is not your own origin. |
The dev tools panel attaches a stylesheet to its shadow root. Keep the panel out of production builds, as its page describes, and a production policy does not need to allow it.
Stamp a nonce on vendor scripts
If your server creates a nonce for each response and puts it in the
Content-Security-Policy header as 'nonce-<value>', pass the same value to
the provider:
The script loader then sets that nonce on every <script> it adds, for both
src and inline textContent scripts. A nonce on one scripts entry wins
for that script. The components put it on the <style> elements they add.
The provider reads nonce once, when it is created.
The nonce covers only the elements c15t creates. A script that a vendor's
script loads by itself does not get it; allow that host in script-src, or
add 'strict-dynamic' so scripts loaded by a trusted script are trusted too.
With 'strict-dynamic', browsers ignore host allowlists in script-src, so
every script on the page needs the nonce or a hash.
Verify the policy
Enforce the policy, not report-only, and open the console with site data cleared:
- The banner renders with its styles, and no
Refused tomessage mentions a c15t element, a<style data-c15t-styles>element or@c15t/svelte/styles.css. Open the preference dialog and check again. - Allow a category. Its vendor scripts load, and none are refused. A refused
script from a vendor's own host means that host is missing from
script-src. - Save a choice. The request to
/subjectssucceeds; aconnect-srcviolation means the backend's origin is missing. - Allow the category of a gated embed. It loads; a
frame-srcviolation names the host to add.