# Content-Security-Policy

> Что разрешить для чата, если сайт отдаёт Content-Security-Policy.

Если ваш сайт отдаёт Content-Security-Policy, политика должна разрешать виджету загружать скрипт и обращаться к серверу. Если политики нет, эту страницу можно пропустить.

## Что нужно виджету
Добавьте эти источники в директивы вашей политики. Свои источники оставьте как есть:

| Директива | Добавить | Зачем |
|---|---|---|
| `script-src` | `https://api.tolkly.io https://ru.tolkly.io` | `/widget.js` и части, которые он импортирует из `/widget/` |
| `connect-src` | `https://api.tolkly.io https://ru.tolkly.io` | настройки проекта; вопросы и ответы потоком; живой поток разговора (server-sent events); загрузка и скачивание файлов |
| `img-src` | `https://api.tolkly.io https://ru.tolkly.io blob:` | картинки в разговоре, по подписанным ссылкам (`/v1/files/…`); `blob:` — превью картинки до отправки |

Менять `style-src` не нужно, `'unsafe-inline'` тоже не нужен. Виджет задаёт стили своего shadow root через constructed stylesheets (`adoptedStyleSheets`). Политика их не блокирует. Они работают в Chrome 73+, Edge 79+, Firefox 101+ и Safari 16.4+. Про старый Safari — в разделе [nonce](#nonces).

`https://api.tolkly.io https://ru.tolkly.io` выше — все адреса, с которых может загрузиться виджет: `widget.js` загружает чат
со следующего, если первый не ответил вовремя ([посетители из России](https://tolkly.io/docs/ru/install.md#russia)). Код
установки, скопированный до 10 ноября 2026 года, может начинаться со встроенного `<script>` —
загрузчика. Он продолжает работать, с вашим nonce или его хешем в `script-src`, как раньше;
обычной строке из раздела [установка](https://tolkly.io/docs/ru/install.md#the-two-lines) не нужно ни то, ни другое.

Больше ничего не нужно:

- `'unsafe-eval'` не нужен.
- `font-src` не нужен. Чат берёт шрифт сайта по имени (`--tolkly-font`) и не загружает файлы шрифтов.
- `frame-src` не нужен. Виджет не использует фреймы.
- `media-src` не нужен. Звуки создаются прямо в браузере.

## Заголовок целиком
```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:
```

Та же политика мета-тегом, в `<head>` перед любым скриптом:

```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:">
```

## Nonce и strict-dynamic
В Safari до 16.4 нет constructed stylesheets. Там виджет добавляет стили элементом `<style>` в свой shadow root, и на него действует `style-src`. Если ваша политика задаёт nonce для стилей, поставьте тот же nonce на тег. Тогда и в этих браузерах чат будет со стилями:

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

`SERVER_NONCE` — это nonce, который ваш сервер создаёт для каждого ответа.

- Если у тега нет атрибута `nonce`, виджет берёт nonce первого `<script nonce>` на странице.
- Если nonce нет совсем, старый Safari покажет чат без стилей, если в `style-src` нет `'unsafe-inline'`. Современных браузеров это не касается.

Ещё два случая:

- **`script-src` с `'strict-dynamic'`:** адреса вроде `https://api.tolkly.io` в нём не действуют. Поставьте ваш nonce на тег `<script>` виджета. Тогда `'strict-dynamic'` пропустит и части, которые скрипт импортирует.
- **Только `default-src`:** он заменяет все директивы выше, которых нет в политике. Добавьте источники в `default-src` или пропишите эти директивы отдельно.

## 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;
```

`add_header` внутри `location` заменяет все `add_header` блока `server`. Повторите политику в этом `location` или задавайте её в одном месте.

## 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 }] }]
  },
}
```

Добавьте эти источники к тому, что приложение уже разрешает. Next.js нужны и свои источники, которых в примере нет: для его встроенных скриптов и стилей или nonce из middleware.

## Если политика не может это разрешить
Если политика блокирует чат, на этих страницах он не работает. Браузер не пропускает скрипт или запросы и пишет об этом в консоли. Не ослабляйте политику сами. Расскажите владельцу сайта, что нужно чату, и пусть решает он.
