# Content-Security-Policy

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

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:

| Directive | Add | What for |
|---|---|---|
| `script-src` | `https://api.tolkly.io https://ru.tolkly.io` | `/widget.js` and the parts it imports from `/widget/` |
| `connect-src` | `https://api.tolkly.io https://ru.tolkly.io` | project settings; questions and streamed answers; the live conversation stream (server-sent events); file uploads and downloads |
| `img-src` | `https://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](#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](https://tolkly.io/docs/en/install.md#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](https://tolkly.io/docs/en/install.md#the-two-lines) 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

```js
// 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.
