Svelte Advanced
Content Security Policy
Set the policy where you host the app
A Svelte app built with Vite has no server of its own, so the policy comes
from your host or CDN as a Content-Security-Policy header, or from a
<meta http-equiv="Content-Security-Policy"> tag in index.html. Test it
against vite build and vite preview. The dev server injects CSS through
<style> elements, so a strict policy breaks vite dev even when production
works.
A static host serves the same file to everyone and cannot put a fresh nonce
in each response, so allow each vendor's script host in script-src instead
of relying on the provider's nonce.
In a Svelte app without server rendering, Svelte sets every style value
through the DOM, which a policy does not block, so c15t needs no
'unsafe-inline' for style attributes.
c15t's components add their rules to <head> as <style> elements, and a
style-src without 'unsafe-inline' blocks those. A static host has no
nonce to give the provider, so set styles={false} on
ConsentProvider and import @c15t/svelte/styles.css from your
entry module. 'self' covers that stylesheet.
Allow the early /init script that consentManifest() adds to index.html
in script-src by the hash in the browser's Refused to execute inline script error. If the policy blocks
it, the provider sends /init itself.
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.