Appearance
Content-Security-Policy
If your site sends a Content-Security-Policy, add your chat server to these directives:
| Directive | Why |
|---|---|
script-src https://chat.example.com | widget.js. With a nonce-based policy, also put your nonce on the script tag. |
connect-src https://chat.example.com | widget.js fetches the bot's settings from the server. |
frame-src https://chat.example.com | The chat window is an iframe on the server. |
img-src https://chat.example.com | The launcher shows the agent's face picture, from /core/<hash>/assets/skins/ (or /v1/widget/faces/ for a face made from a photo). |
That's all. The chat's WebSocket, audio and camera all belong to the chat window, which runs on the chat server under its own policy, so your connect-src doesn't need wss:.
A complete strict policy, with a nonce and Trusted Types enforced:
text
default-src 'self';
script-src 'nonce-RANDOM' https://chat.example.com;
connect-src 'self' https://chat.example.com;
frame-src https://chat.example.com;
img-src 'self' data: https://chat.example.com;
style-src 'self' 'nonce-RANDOM';
object-src 'none';
base-uri 'none';
require-trusted-types-for 'script';
trusted-types 'none'html
<script nonce="RANDOM" src="https://chat.example.com/widget.js" data-bot="pk_your_bot_id" async></script>examples/strict-csp/ in the repo serves a page with exactly this policy (headers.json), and the end-to-end tests check that the chat works there with no violations.
Styles
No style-src change is needed. The launcher's styles are constructable stylesheets inside a shadow root, which a CSP doesn't govern; positions are set through the CSSOM, not style attributes. In browsers without constructable stylesheets, widget.js falls back to a <style> element with the nonce from its own script tag. Only with no nonce in such a browser does it link https://chat.example.com/widget.css, which then needs the host in style-src.
Nonces and strict-dynamic
widget.js reads the nonce from its own <script> tag. With 'strict-dynamic', the nonce on the tag is enough (host lists are ignored under it). With the npm package, pass nonce to bootWirefaceChat() or loadWirefaceChat(): it goes on the script tag the package adds.
Trusted Types
widget.js never assigns HTML strings or script URLs: it builds its elements with createElement and textContent. It runs under require-trusted-types-for 'script' with trusted-types 'none'.
The npm package adds widget.js by setting a script element's src, which Trusted Types guards. Where the browser has Trusted Types, it makes the URL through a policy named wireface-chat, which only ever passes the widget.js URL built from your host. Allow that name in your policy:
text
require-trusted-types-for 'script';
trusted-types wireface-chat(Add it to the names you already allow, e.g. trusted-types my-app wireface-chat.) If the name isn't allowed, the package doesn't create the policy and the browser blocks the script. The script tag in your HTML needs no policy at all.
The chat's own policy
The other direction is handled for you: the chat window is sent with frame-ancestors built from the bot's allowed origins, so other sites can't put your bot in a frame. Every page up the frame chain must be allowed, so a page of yours shown inside someone else's iframe can't show the chat.
Microphone and camera
The chat window's iframe asks for microphone, camera, autoplay and clipboard-write. If your site sends a Permissions-Policy header that limits those, delegate them to the chat host, for example:
text
Permissions-Policy: microphone=(self "https://chat.example.com"), camera=(self "https://chat.example.com")Without that, voice fails with mic_denied and the camera with camera_denied.
Finding what's blocked
The browser console names each blocked request. Typical signs:
- the launcher never appears and the console shows
network:connect-srcis missing the host; - the launcher appears but the window stays empty, then
frame_blockedafter 15 seconds:frame-src; - the launcher shows no face picture:
img-src.
Try a new policy as Content-Security-Policy-Report-Only first to see what it would block.