For developers

Content-Security-Policy

What to allow for the chat when a site sends a Content-Security-Policy.

On this page

If your site sends a Content-Security-Policy, it must let the widget load its script and talk to its server. If your site has no policy, skip this page.

What the widget needs

Add these sources to the directives in your policy. Keep your own sources as they are:

DirectiveAddWhat for
script-srchttps://api.tolkly.io https://ru.tolkly.io/widget.js and the parts it imports from /widget/
connect-srchttps://api.tolkly.io https://ru.tolkly.ioproject settings; questions and streamed answers; the live conversation stream (server-sent events); file uploads and downloads
img-srchttps://api.tolkly.io https://ru.tolkly.io blob:pictures in the conversation, by signed links (/v1/files/…); blob: previews a picture before it is sent

You do not need to change style-src, and you do not need 'unsafe-inline'. The widget styles its shadow root with constructed stylesheets (adoptedStyleSheets). A policy does not block them. They work in Chrome 73+, Edge 79+, Firefox 101+ and Safari 16.4+. For older Safari, see nonces.

https://api.tolkly.io https://ru.tolkly.io above are every address the widget may load from: widget.js loads the chat from the next one when the first does not answer in time (visitors in Russia). An install code copied before November 10, 2026 may start with an inline <script>, a loader. It keeps working, with your nonce or its hash in script-src as before; the plain line from install needs neither.

Nothing else is needed:

  • No 'unsafe-eval'.
  • No font-src. The chat uses the site's own font by name (--tolkly-font) and loads no font files.
  • No frame-src. The widget uses no frames.
  • No media-src. Its sounds are made in the browser.

A full header

HTTP
Content-Security-Policy: default-src 'self'; script-src 'self' https://api.tolkly.io https://ru.tolkly.io; connect-src 'self' https://api.tolkly.io https://ru.tolkly.io; img-src 'self' https://api.tolkly.io https://ru.tolkly.io blob:

The same policy as a meta tag, in <head> before any script:

HTML
<meta http-equiv="Content-Security-Policy"
      content="default-src 'self'; script-src 'self' https://api.tolkly.io https://ru.tolkly.io; connect-src 'self' https://api.tolkly.io https://ru.tolkly.io; img-src 'self' https://api.tolkly.io https://ru.tolkly.io blob:">

Nonces and strict-dynamic

Safari before 16.4 has no constructed stylesheets. There the widget adds its styles as a <style> element in its shadow root, and style-src applies to it. If your policy uses a nonce for styles, put the same nonce on the tag. Then the chat is styled in those browsers too:

HTML
<tolkly-chat endpoint="https://api.tolkly.io" project="your-project" nonce="SERVER_NONCE"></tolkly-chat>

SERVER_NONCE is the nonce your server makes for each response.

  • If the tag has no nonce attribute, the widget takes the nonce of the page's first <script nonce>.
  • If there is no nonce at all, older Safari shows the chat unstyled, unless style-src has 'unsafe-inline'. Current browsers are not affected.

Two more cases:

  • script-src with 'strict-dynamic': host sources such as https://api.tolkly.io are ignored. Put your nonce on the widget's <script> tag. 'strict-dynamic' then lets in the parts it imports.
  • Only default-src: it stands in for every directive above that your policy does not name. Add the sources to default-src, or add the directives separately.

nginx

nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://api.tolkly.io https://ru.tolkly.io; connect-src 'self' https://api.tolkly.io https://ru.tolkly.io; img-src 'self' https://api.tolkly.io https://ru.tolkly.io blob:" always;

An add_header inside a location replaces all add_header lines of the server block. Repeat the policy in that location, or set it in one place only.

Next.js

JavaScript
// next.config.mjs
const csp = [
  "default-src 'self'",
  "script-src 'self' https://api.tolkly.io https://ru.tolkly.io",
  "connect-src 'self' https://api.tolkly.io https://ru.tolkly.io",
  "img-src 'self' https://api.tolkly.io https://ru.tolkly.io blob:",
].join('; ')

export default {
  async headers() {
    return [{ source: '/:path*', headers: [{ key: 'Content-Security-Policy', value: csp }] }]
  },
}

Add these sources to what your app already allows. Next.js also needs sources of its own, which this example leaves out: its inline scripts and styles, or a nonce from middleware.

If the policy cannot allow this

If the policy blocks the chat, the chat does not work on those pages. The browser refuses the script or the requests and reports it in the console. Do not loosen the policy on your own. Tell the site's owner what the chat needs and let them decide.