Собственный домен и прокси первой стороны
Как предоставлять файлы виджета, подключение чата, офлайн-форму и Smart Answer через поддомен сайта: ключ прокси, настоящий адрес посетителя и готовые шаблоны nginx, Apache и Caddy.
При собственном домене чат работает через поддомен сайта, например chat.your-company.com. Ваш сервер принимает запросы виджета и передаёт их reactive.chat как обратный прокси. Эта страница предназначена для администратора сервера. Краткая версия для редакторов — Собственный домен (first-party).
Что проходит через поддомен
- Файлы виджета —
loader.js, загружаемое после него ядро (core.<hash>.js) и остальные модули. - Подключение чата — WebSocket по
/rc_wss/, включая резервный канал HTTP по/rc_wss/h/и загрузки файлов по/rc_wss/upload/. - Офлайн-форма —
POST /api/offline/<SITE_KEY>, если подключиться к чату нельзя. - Smart Answer —
/api/suche/, если вы используете ответ ИИ в поиске по сайту.
Что вы получаете:
- Нет запросов к сторонним доменам. По этим маршрутам браузеры посетителей обращаются только к вашему поддомену.
- Один адрес в Content Security Policy. В
script-srcиconnect-srcуказывается поддомен вместо адресов reactive.chat (подробнее: Безопасность и CSP). - Доходит настоящий адрес посетителя. Прокси сообщает его специальным заголовком, защищённым секретным ключом. Ограничение частоты и блокировки тогда учитывают посетителя, а не прокси.
Чего это не означает:
- Это не самостоятельный хостинг. Разговоры, вложения и настройки по-прежнему обрабатываются и хранятся в reactive.chat.
- Это ничего не говорит о согласии. Собственный домен не меняет того, кто обрабатывает данные.
- Прокси видит трафик. TLS заканчивается на прокси; не записывайте там тела запросов и исключите
/rc_wss/h/из журнала доступа (эти запросы несут краткоживущий токен соединения в строке запроса).
Требования
- Сервер или обратный прокси, публично доступный по HTTPS на порту 443 и способный открывать исходящие HTTPS-соединения. Есть шаблоны для nginx (с модулем ssl), Apache 2.4.47 и новее и Caddy 2.
- Для Apache — модули
mod_ssl,mod_proxy,mod_proxy_http,mod_proxy_wstunnel,mod_headersиmod_setenvif. - Поддомен только для чата. Чату нужен весь хост; путь вроде
www.your-company.com/chat/не подойдёт. Вводите лишь имя, безhttps://, порта и пути. Не разрешены IP-адреса, внутренние окончания вроде.local,.lan,.internalи имена в доменеreactive.chat. - Тот же домен, что у сайта. Чат относится к первой стороне, только если поддомен принадлежит домену сайта, на котором он встроен.
- DNS (A и/или AAAA) только с публичными адресами.
- TLS-сертификат для поддомена (шаблоны nginx/Apache ожидают пути certbot; Caddy получает его сам).
- Право изменять проект.
Настройка
- В аккаунте откройте проект, раздел Встраивание → Настроить собственный домен.
- В 1. Хост укажите имя, например
chat.your-company.com, и сохраните. - В 2. Ключ прокси нажмите Создать ключ. Ключ начинается с
rcpx_и показывается ровно один раз; сохраняется только хеш. Ему место исключительно в конфигурации прокси, никогда на веб-странице, в браузерном скрипте или репозитории. - В 3. Конфигурация сервера возьмите шаблон своего сервера. Сразу после создания ключа он уже в шаблоне; позднее там
__RC_PROXY_KEY__. Проверьте конфигурацию (nginx -tилиapachectl configtest) и перезагрузите сервер. - В 5. Проверка нажмите Проверить сейчас (не чаще раза в 10 секунд). Сообщение не создаётся.
- Только когда все пять шагов показывают «OK», замените код встраивания кодом из 4. Код встраивания:
<script src="https://chat.your-company.com/loader.js"
data-rc-site-key="SITE_KEY"
data-rc-ws-url="wss://chat.your-company.com/rc_wss/"
async></script>
Виджет берёт расположение файлов из src, подключение чата из data-rc-ws-url и выводит из него адреса офлайн-формы, загрузок и резервного канала HTTP. Остальные атрибуты остаются прежними (Справочник по тегу скрипта).
Что прокси должен пересылать
/rc_wss/→ сервер чатаhttps://ws.reactive.chat/rc_wss/, с переключением на WebSocket, большим тайм-аутом (шаблоны: 3600 с), без буферизации и с лимитом тела не менее 32 МБ (загрузки)./api/offline/→https://ws.reactive.chat/api/offline/(небольшое тело, шаблон: 32 кБ, 30 с)./api/suche/→https://ws.reactive.chat/api/suche/, без буферизации (ответ — поток Server-Sent Events), тело до 8 кБ, 120 с.- Всё остальное → сеть доставки
https://cdn.reactive.chat/.
Для всех маршрутов:
- Заголовок Host и SNI содержат имя цели, а не ваш поддомен. На неизвестный хост приходит HTTP 421.
- Сертификат цели проверяется.
- Без cookie и учётных данных. Браузерные
CookieиAuthorizationне передаются,Set-Cookieне возвращается. - Без перенаправлений. Кеш не нужен; если вы его создаёте, задайте короткий срок для
loader.js: у файла нет номера версии в имени (у ядра есть, см. Архитектура).
Для /rc_wss/, /api/offline/ и /api/suche/ прокси задаёт два заголовка в каждом запросе:
- X-RC-Proxy-Key — ваш ключ
rcpx_…. - X-RC-Client-IP — адрес посетителя, который видит прокси.
«Задаёт» означает, что всё присланное браузером под этими именами перезаписывается. На маршруте к сети доставки оба очищаются.
За балансировщиком нагрузки или CDN прокси сначала видит адрес этого устройства. Тогда настройте получение настоящего адреса: nginx real_ip (set_real_ip_from, real_ip_header), Apache mod_remoteip (RemoteIPHeader, RemoteIPTrustedProxy), Caddy trusted_proxies и {client_ip} вместо {remote_host}. Проверка этого не обнаруживает: она видит лишь, что задан корректный адрес.
Шаблон nginx
Страница аккаунта уже подставляет хост, целевые адреса и ключ; лучше копировать шаблон оттуда. Здесь версия для справки; места для подстановки — chat.your-company.com, SITE_KEY и __RC_PROXY_KEY__. В RHEL/CentOS набор CA находится в /etc/pki/tls/certs/ca-bundle.crt.
server {
listen 80;
listen [::]:80;
server_name chat.your-company.com;
location /.well-known/acme-challenge/ { root /var/www/html; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name chat.your-company.com;
ssl_certificate /etc/letsencrypt/live/chat.your-company.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.your-company.com/privkey.pem;
# HTTP fallback (SSE / long polling): token in the query, do not log
location /rc_wss/h/ {
access_log off;
proxy_pass https://ws.reactive.chat/rc_wss/h/;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host ws.reactive.chat;
proxy_ssl_server_name on;
proxy_ssl_name ws.reactive.chat;
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_set_header X-RC-Proxy-Key "__RC_PROXY_KEY__";
proxy_set_header X-RC-Client-IP $remote_addr;
proxy_set_header Cookie "";
proxy_set_header Authorization "";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_cache off;
}
# Chat connection (WebSocket) and uploads
location /rc_wss/ {
proxy_pass https://ws.reactive.chat/rc_wss/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host ws.reactive.chat;
proxy_ssl_server_name on;
proxy_ssl_name ws.reactive.chat;
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_set_header X-RC-Proxy-Key "__RC_PROXY_KEY__";
proxy_set_header X-RC-Client-IP $remote_addr;
proxy_set_header Cookie "";
proxy_set_header Authorization "";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_cache off;
client_max_body_size 32m;
proxy_request_buffering off;
}
# Offline form
location /api/offline/ {
proxy_pass https://ws.reactive.chat/api/offline/;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host ws.reactive.chat;
proxy_ssl_server_name on;
proxy_ssl_name ws.reactive.chat;
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_set_header X-RC-Proxy-Key "__RC_PROXY_KEY__";
proxy_set_header X-RC-Client-IP $remote_addr;
proxy_set_header Cookie "";
proxy_set_header Authorization "";
proxy_hide_header Set-Cookie;
client_max_body_size 32k;
proxy_read_timeout 30s;
}
# Smart Answer (website search), answer as a stream
location /api/suche/ {
proxy_pass https://ws.reactive.chat/api/suche/;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host ws.reactive.chat;
proxy_ssl_server_name on;
proxy_ssl_name ws.reactive.chat;
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_set_header X-RC-Proxy-Key "__RC_PROXY_KEY__";
proxy_set_header X-RC-Client-IP $remote_addr;
proxy_set_header Cookie "";
proxy_set_header Authorization "";
proxy_hide_header Set-Cookie;
client_max_body_size 8k;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 120s;
}
# Widget files
location / {
proxy_pass https://cdn.reactive.chat/;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host cdn.reactive.chat;
proxy_ssl_server_name on;
proxy_ssl_name cdn.reactive.chat;
proxy_ssl_verify on;
proxy_ssl_verify_depth 3;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_set_header X-RC-Proxy-Key "";
proxy_set_header X-RC-Client-IP "";
proxy_set_header Cookie "";
proxy_set_header Authorization "";
proxy_hide_header Set-Cookie;
}
}
Шаблон Apache (2.4.47 и новее)
Только VirtualHost для порта 443; перенаправление с порта 80 и сертификат настройте как для других хостов. SSLProxyVerifyDepth 5 обязателен; иначе Apache проверяет цепочку цели лишь на один уровень и сообщает «Certificate Chain too long».
<VirtualHost *:443>
ServerName chat.your-company.com
SetEnvIf Request_URI "^/rc_wss/h/" rc_no_log
CustomLog ${APACHE_LOG_DIR}/chat.your-company.com_access.log combined env=!rc_no_log
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/chat.your-company.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/chat.your-company.com/privkey.pem
SSLProxyEngine on
SSLProxyVerify require
SSLProxyVerifyDepth 5
SSLProxyCheckPeerName on
SSLProxyCACertificateFile /etc/ssl/certs/ca-certificates.crt
ProxyRequests Off
ProxyPreserveHost Off
ProxyAddHeaders Off
RequestHeader unset X-RC-Proxy-Key early
RequestHeader unset X-RC-Client-IP early
RequestHeader unset Cookie early
RequestHeader unset Authorization early
ProxyPass "/rc_wss/" "https://ws.reactive.chat/rc_wss/" upgrade=websocket timeout=3600 flushpackets=on
ProxyPass "/api/offline/" "https://ws.reactive.chat/api/offline/" timeout=30
ProxyPass "/api/suche/" "https://ws.reactive.chat/api/suche/" timeout=120 flushpackets=on
ProxyPass "/" "https://cdn.reactive.chat/"
<Location "/rc_wss/">
RequestHeader set X-RC-Proxy-Key "__RC_PROXY_KEY__"
RequestHeader set X-RC-Client-IP "expr=%{REMOTE_ADDR}"
</Location>
<Location "/api/offline/">
RequestHeader set X-RC-Proxy-Key "__RC_PROXY_KEY__"
RequestHeader set X-RC-Client-IP "expr=%{REMOTE_ADDR}"
Header unset Set-Cookie
</Location>
<Location "/api/suche/">
RequestHeader set X-RC-Proxy-Key "__RC_PROXY_KEY__"
RequestHeader set X-RC-Client-IP "expr=%{REMOTE_ADDR}"
Header unset Set-Cookie
</Location>
</VirtualHost>
Apache до 2.4.47 не поддерживает upgrade=websocket. В таком случае замените первую строку ProxyPass на:
RewriteEngine on
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule ^/rc_wss/(.*) "wss://ws.reactive.chat/rc_wss/$1" [P,L]
ProxyPass "/rc_wss/" "https://ws.reactive.chat/rc_wss/" timeout=3600 flushpackets=on
Шаблон Caddy 2
header_up со значением заменяет всё, что браузер прислал под этим именем. Если включаете журналы доступа, исключите /rc_wss/h/* (log_skip, до Caddy 2.8 — skip_log).
chat.your-company.com {
handle /rc_wss/* {
reverse_proxy https://ws.reactive.chat {
header_up Host ws.reactive.chat
header_up X-RC-Proxy-Key "__RC_PROXY_KEY__"
header_up X-RC-Client-IP {remote_host}
header_up -Cookie
header_up -Authorization
transport http {
tls_server_name ws.reactive.chat
}
flush_interval -1
}
}
handle /api/offline/* {
reverse_proxy https://ws.reactive.chat {
header_up Host ws.reactive.chat
header_up X-RC-Proxy-Key "__RC_PROXY_KEY__"
header_up X-RC-Client-IP {remote_host}
header_up -Cookie
header_up -Authorization
header_down -Set-Cookie
transport http {
tls_server_name ws.reactive.chat
}
}
}
handle /api/suche/* {
reverse_proxy https://ws.reactive.chat {
header_up Host ws.reactive.chat
header_up X-RC-Proxy-Key "__RC_PROXY_KEY__"
header_up X-RC-Client-IP {remote_host}
header_up -Cookie
header_up -Authorization
header_down -Set-Cookie
transport http {
tls_server_name ws.reactive.chat
}
flush_interval -1
}
}
handle {
reverse_proxy https://cdn.reactive.chat {
header_up Host cdn.reactive.chat
header_up -X-RC-Proxy-Key
header_up -X-RC-Client-IP
header_up -Cookie
header_up -Authorization
header_down -Set-Cookie
transport http {
tls_server_name cdn.reactive.chat
}
}
}
}
Пять шагов проверки
Каждый шаг показывает «OK», «Ошибка», «Примечание» или «Не проверено». Проверка пройдена только при пяти «OK». Используется только HTTPS на порту 443, без перенаправлений, 5 секунд на шаг.
- Разрешение имени — есть A/AAAA, все адреса публичные. Следующие шаги подключаются именно к этому адресу.
- loader.js через ваш домен — файл загружается и сравнивается с нашим. Без подключения, ответа или действительного сертификата остальные шаги пропускаются. Перенаправление — ошибка; отличие файла указывает на кеш или неверную цель для
/. - Ключ и адрес посетителя — вызов
/rc_wss/eigene-domain-probe. Возможные ошибки: нетX-RC-Proxy-Key; ключ неизвестен (сразу после создания возможна задержка до 30 секунд);X-RC-Client-IPотсутствует или неверен; ключ относится к другому проекту. Примечание — если ключ действителен, но зарегистрирован для другого хоста. - WebSocket —
wss://chat.your-company.com/rc_wss/должен ответить HTTP 101 с подпротоколомrc.v1. - Офлайн-форма —
GET /api/offline/SITE_KEYдолжен ответить 405 (конечная точка принимает только POST; так сообщение не создаётся).
Проверочный адрес из шага 3 можно вызвать самостоятельно:
curl -s https://chat.your-company.com/rc_wss/eigene-domain-probe
При правильной настройке:
{"rc":"eigene-domain-probe","v":1,"schluessel":"gueltig","client_ip":"gesetzt","site":"SITE_KEY","host":"chat.your-company.com"}
- schluessel —
gueltig(действителен),unbekannt(неизвестен) илиfehlt(отсутствует). - client_ip —
gesetzt(задан),fehlt(отсутствует) илиungueltig(некорректен). - site, host — только при действительном ключе. Ответ никогда не возвращает адрес.
Ключ прокси
- Ротация: Создать новый ключ немедленно выдаёт новый; прежний действует 24 часа. Одновременно действуют не более двух ключей; повторная ротация за эти 24 часа немедленно аннулирует самый старый.
- Удаление: Удалить ключ немедленно аннулирует его (включая старый после ротации). Удалить собственный домен удаляет хост и ключ вместе; сначала верните код на сайте к обычному виду.
- Нет ключа или он неверен: чат продолжает работать, но оба заголовка игнорируются. Все посетители отображаются с адресом прокси, и защита от злоупотреблений, учитывающая адреса, может ограничить весь прокси, а значит сразу всех посетителей.
- Один проект на ключ: если виджет с ключом сайта другого проекта подключается через прокси с действительным ключом, соединение отклоняется. Для ещё одного проекта нужен отдельный поддомен со своим ключом.
- Ключ утёк: знающий его может указывать произвольные адреса посетителей для проекта. Удалите, создайте новый, установите.
Устранение неполадок
- HTTP 421 (Misdirected Request): прокси отправляет ваше имя как хост вместо имени цели. nginx:
proxy_set_header Hostплюсproxy_ssl_server_name on/proxy_ssl_name; Apache:ProxyPreserveHost Off; Caddy:header_up Hostиtls_server_name. - HTTP 502 / ошибка TLS к цели: в Apache нет
SSLProxyVerifyDepth 5; в nginx неверен путьproxy_ssl_trusted_certificateили нетproxy_ssl_server_name on; исходящий HTTPS заблокирован. - HTTP 413 при загрузке: без настройки nginx принимает лишь 1 МБ; задайте
client_max_body_size 32mв разделе/rc_wss/(и не меньший лимит у вышестоящего балансировщика). - Нет переключения на WebSocket:
Upgrade/Connectionне передаются. Чат тогда работает через резервный канал HTTP, но подключается после двух неудачных попыток WebSocket. - 401 у офлайн-формы: перед
/api/offline/стоит парольная защита. - 403 с примечанием об источнике: в Разрешённых источниках проекта нет домена сайта (не поддомена чата!).
- 403 «Ответ не от reactive.chat»: межсетевой экран или правило доступа перед целью отклоняет запрос.
- DNS указывает на частный адрес (
10.0.0.0/8,192.168.0.0/16,100.64.0.0/10,fc00::/7…): публичная запись должна быть публичной.
Далее: Транспорт и протокол · Безопасность и CSP
Обновлено: