Чим ми можемо допомогти?

Стисло опишіть питання; ми відповімо електронною поштою.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
На головну

Власний домен (перша сторона)

Надавайте віджет, з’єднання чату й офлайн-форму з піддомену свого сайту: вимоги, налаштування, шаблони nginx, Apache й Caddy та усунення несправностей.

Із власним доменом чат працює через піддомен вашого сайту, наприклад chat.your-domain.com. Ваш сервер приймає запити віджета й передає їх до reactive.chat як зворотний проксі. Цю сторінку написано для того, хто обслуговує цей сервер.

Що дає власний домен

Через ваш піддомен проходять:

У результаті:

Чого це не означає

Вимоги

За балансувальником навантаження або CDN ваш проксі спочатку бачить адресу цього пристрою, а не відвідувача. Тоді налаштуйте отримання справжньої адреси відвідувача: у nginx — модуль real_ip (set_real_ip_from, real_ip_header), в Apache — mod_remoteip (RemoteIPHeader, RemoteIPTrustedProxy), у Caddy — параметр trusted_proxies і заповнювач {client_ip} замість {remote_host}. Наша перевірка не може виявити цей випадок: вона бачить лише дійсну адресу, а не те, чия вона.

Покрокове налаштування

  1. Відкрийте сторінку. Відкрийте проєкт в обліковому записі. У розділі «Вбудовування» є посилання Налаштувати власний домен.
  2. Введіть хост. У розділі «1. Хост» введіть ім’я, наприклад chat.your-domain.com, і збережіть.
  3. Створіть ключ. У розділі «2. Ключ проксі» натисніть Створити ключ. Ключ починається з rcpx_ і показується рівно один раз; ми зберігаємо лише його хеш. Одразу скопіюйте його. Він секретний і має бути лише в конфігурації проксі — ніколи на вебсторінці, у браузерному скрипті чи репозиторії.
  4. Вставте шаблон. У розділі «3. Конфігурація сервера» є готові шаблони nginx, Apache та Caddy. Одразу після створення ключа вони вже містять його; пізніше показують __RC_PROXY_KEY__, який потрібно замінити ключем. За потреби змініть шляхи сертифіката, перевірте конфігурацію (nginx -t або apachectl configtest) і перезавантажте сервер.
  5. Перевірте. У розділі «5. Перевірка» натисніть Перевірити зараз. Ми звернемося до піддомену ззовні й окремо перевіримо п’ять шляхів (див. нижче). Повідомлення не створюється. Наступна перевірка можлива щонайраніше через 10 секунд.
  6. Замініть фрагмент. Лише коли всі п’ять кроків покажуть «OK», замініть поточний фрагмент вбудовування на сайті фрагментом із розділу «4. Фрагмент вбудовування».

Новий фрагмент виглядає так — обидві адреси вказують на ваш піддомен:

<script src="https://chat.your-domain.com/loader.js"
        data-rc-site-key="SITE_KEY"
        data-rc-ws-url="wss://chat.your-domain.com/rc_wss/"
        async></script>

Віджет визначає розташування своїх файлів через src, а з’єднання чату — через data-rc-ws-url, з якого виводить адресу офлайн-форми. Інші атрибути, як-от data-rc-locale, не змінюються; їх описано в розділі Вбудовування віджета. Фрагмент на сторінці облікового запису також містить колір бренду.

П’ять кроків перевірки

Кожен крок показує «OK», «Помилка», «Примітка» або «не перевірено». Перевірку пройдено лише тоді, коли всі п’ять показують «OK»; примітка не зараховується як успіх. Перевірки використовують лише HTTPS на порту 443, не переходять за перенаправленнями й відводять кожному кроку 5 секунд.

  1. Визначення адреси за ім’ям. Ім’я має мати запис DNS, і всі адреси мають бути публічними. Повідомляється, якщо не знайдено ані A, ані AAAA або хоча б одна адреса приватна, локальна чи зарезервована — такі адреси ми не перевіряємо. Усі наступні кроки підключаються саме до адреси, перевіреної тут.
  2. loader.js через ваш домен. Ми завантажуємо https://chat.your-domain.com/loader.js і порівнюємо файл із нашим. Якщо з’єднання, відповіді або дійсного сертифіката немає, решта кроків пропускається («не перевірено»). Якщо проксі відповідає перенаправленням замість файлу, це помилка. Якщо файл відрізняється від нашого, причина зазвичай у кеші проксі або неправильній цілі для /.
  3. Ключ і адреса відвідувача. Ми звертаємося до адреси перевірки під /rc_wss/. Наш сервер відповідає, чи отримав дійсний ключ і дійсну адресу відвідувача та якому проєкту належить ключ. Можливі помилки: відсутній заголовок X-RC-Proxy-Key; ключ невідомий (помилка введення, застарів або видалений; одразу після створення може минути до 30 секунд); заголовок X-RC-Client-IP відсутній або не містить дійсної адреси; ключ належить іншому проєкту. Якщо ключ дійсний, але зареєстрований для іншого хоста, з’являється примітка.
  4. З’єднання чату (WebSocket). Ми відкриваємо з’єднання WebSocket через wss://chat.your-domain.com/rc_wss/ та очікуємо HTTP 101 із протоколом rc.v1. «Немає переходу на WebSocket» означає, що проксі не передає заголовки Upgrade і Connection.
  5. Офлайн-форма. Ми звертаємося до https://chat.your-domain.com/api/offline/SITE_KEY через GET. Наша кінцева точка відповідає HTTP 405, бо приймає лише POST; так ми знаємо, що запит доходить до нас, не створюючи повідомлення. Інші результати: джерело відхилено (примітка, див. усунення несправностей), проєкт неактивний, ціль потребує автентифікації (HTTP 401).

Кроки 2–5 також можуть повідомляти HTTP 421 (заголовок Host, див. усунення несправностей). Якщо крок 3 або 5 повідомляє «Відповідь не від reactive.chat», цей шлях у проксі вказує на неправильну ціль.

Що має пересилати проксі

Для всіх трьох шляхів:

Для /rc_wss/ і /api/offline/ проксі також задає в кожному запиті два заголовки:

«Задає» означає: усе, що браузер надсилає під цими назвами, перезаписується й ніколи не доходить до нас. На шляху до мережі доставки обидва заголовки очищуються; ключ надходить лише на сервер чату.

Резервний канал HTTP через ваш домен

Якщо мережа відвідувача не пропускає WebSocket, віджет автоматично переходить на резервний канал HTTP. Він використовує той самий шлях /rc_wss/ і не потребує додаткового запису в проксі. Потрібні дві речі:

Шаблони

Наведені нижче шаблони відповідають шаблонам на сторінці облікового запису. Вона вже заповнює ваш хост, актуальні для облікового запису цільові адреси та, одразу після створення, ключ. Тому найкраще копіювати шаблон звідти; ці версії — довідкові. Заповнювачі: chat.your-domain.com, SITE_KEY та __RC_PROXY_KEY__.

nginx

Розміщується в контексті http, наприклад як /etc/nginx/conf.d/chat.your-domain.com.conf. У RHEL та CentOS пакет CA розташований у /etc/pki/tls/certs/ca-bundle.crt замість /etc/ssl/certs/ca-certificates.crt.

# reactive.chat - own domain (first-party proxy) for site key SITE_KEY
# Host: chat.your-domain.com
#
# Needs nginx >= 1.7 with the ssl module. Put this file into the http
# context (e.g. /etc/nginx/conf.d/chat.your-domain.com.conf), adjust the certificate
# paths if you do not use certbot, then:  nginx -t && systemctl reload nginx
#
# The proxy key is a secret: never put it into a web page or a repository.
# X-RC-Proxy-Key and X-RC-Client-IP are always SET here - whatever a browser
# sends under these names is overwritten.

server {
    listen 80;
    listen [::]:80;
    server_name chat.your-domain.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-domain.com;

    ssl_certificate     /etc/letsencrypt/live/chat.your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/chat.your-domain.com/privkey.pem;

    # Chat connection (WebSocket). Host header and SNI must be the upstream
    # name - reactive.chat answers 421 to any other host.
    # HTTP fallback (Server-Sent Events / long polling): its requests carry a
    # short-lived connection token in the query string - EventSource cannot
    # send headers. Same settings as /rc_wss/ below, but kept out of the
    # access 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;
    }

    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;
        # Unbuffered: if a visitor's network blocks WebSockets, the widget
        # falls back to Server-Sent Events / long polling on this path.
        proxy_buffering off;
        proxy_cache off;
        # File attachments are uploaded through this path as well (PUT
        # /rc_wss/upload/...). nginx allows 1 MB by default; reactive.chat
        # accepts up to 32 MB per file depending on the plan.
        client_max_body_size 32m;
        proxy_request_buffering off;
    }

    # Offline form fallback (HTTP POST from the widget).
    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 for site search (suche.js and the HTTP API). Same rules as
    # the offline route: key and visitor address are set here.
    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;
        # The answer is a stream (Server-Sent Events): pass it on unbuffered.
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 120s;
    }

    # Widget files (loader.js, core.*.js ...). No caching required; if you
    # add a cache, keep it short for loader.js (it is not versioned).
    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;
    }
}
# On RHEL/CentOS the CA bundle is /etc/pki/tls/certs/ca-bundle.crt.
# Behind a load balancer/CDN, $remote_addr is the balancer: configure the
# real_ip module so that it holds the visitor's address.

Apache

Шаблон містить лише VirtualHost для порту 443. Налаштуйте перенаправлення з порту 80 та сертифікат так само, як для інших хостів. Потрібен SSLProxyVerifyDepth 5: без нього Apache перевіряє ланцюжок сертифікатів цілі лише на один рівень, і з’єднання завершується помилкою «Certificate Chain too long».

# reactive.chat - own domain (first-party proxy) for site key SITE_KEY
# Host: chat.your-domain.com
#
# Needs Apache >= 2.4.47 with mod_ssl, mod_proxy, mod_proxy_http,
# mod_proxy_wstunnel, mod_headers and mod_setenvif:
#   a2enmod ssl proxy proxy_http proxy_wstunnel headers setenvif
# Adjust the certificate paths if you do not use certbot, then:
#   apachectl configtest && systemctl reload apache2
#
# The proxy key is a secret: never put it into a web page or a repository.

<VirtualHost *:443>
    ServerName chat.your-domain.com

    # HTTP fallback requests (/rc_wss/h/...) carry a short-lived connection
    # token in the query string - keep them out of the access log.
    SetEnvIf Request_URI "^/rc_wss/h/" rc_no_log
    # ${APACHE_LOG_DIR} is set on Debian/Ubuntu; elsewhere e.g. /var/log/httpd.
    CustomLog ${APACHE_LOG_DIR}/chat.your-domain.com_access.log combined env=!rc_no_log

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/chat.your-domain.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/chat.your-domain.com/privkey.pem

    # Upstream TLS: verified, SNI = upstream name (ProxyPreserveHost Off
    # sends the upstream name as Host - reactive.chat answers 421 otherwise).
    SSLProxyEngine on
    SSLProxyVerify require
    # Apache checks only ONE certificate deep by default; the upstream chain
    # (leaf, intermediate, root) is longer: "Certificate Chain too long".
    SSLProxyVerifyDepth 5
    SSLProxyCheckPeerName on
    SSLProxyCACertificateFile /etc/ssl/certs/ca-certificates.crt
    ProxyRequests Off
    ProxyPreserveHost Off
    ProxyAddHeaders Off

    # Whatever the browser sends under these names never reaches upstream.
    RequestHeader unset X-RC-Proxy-Key early
    RequestHeader unset X-RC-Client-IP early
    RequestHeader unset Cookie early
    RequestHeader unset Authorization early

    # Longest path first. flushpackets=on on /rc_wss/: if a visitor's network
    # blocks WebSockets, the widget falls back to Server-Sent Events / long
    # polling on this path, and those must not be buffered.
    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 has no upgrade=websocket. Instead of the first 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

Caddy отримує сертифікат самостійно; WebSocket не потребує додаткового налаштування. header_up зі значенням замінює все, що браузер надіслав під цією назвою.

# reactive.chat - own domain (first-party proxy) for site key SITE_KEY
# Host: chat.your-domain.com
#
# Caddy 2 obtains the certificate itself. WebSockets need no extra setting.
# The proxy key is a secret: never put it into a web page or a repository.
# header_up with a value REPLACES what the browser sent under that name.
# If you enable access logging (log directive), skip the HTTP fallback
# requests - they carry a short-lived token in the query string:
#   log_skip /rc_wss/h/*        (Caddy < 2.8: skip_log /rc_wss/h/*)

chat.your-domain.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
            }
            # The answer is a stream (Server-Sent Events): pass it on unbuffered.
            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
            }
        }
    }
}

Ключ проксі

Усунення несправностей

HTTP 421 (Misdirected Request)

Наш сервер відхиляє заголовок Host: проксі надсилає ваше ім’я замість імені цілі. Заголовок Host і SNI мають бути ім’ям цілі — у 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 або помилки SSL до цілі

Проксі не може встановити TLS-з’єднання з нами або не довіряє сертифікату.

HTTP 413 під час завантаження вкладень

Завантаження йдуть через /rc_wss/. Без окремого налаштування nginx приймає лише 1 МБ; тому шаблон задає client_max_body_size 32m у розділі /rc_wss/. Якщо перед проксі є балансувальник навантаження або CDN, його ліміт має бути щонайменше таким самим.

З’єднання чату без переходу на WebSocket

Перевірка повідомляє «Немає переходу на WebSocket» із отриманим натомість станом HTTP. Тоді чат працює через резервний канал HTTP, але підключається лише після двох невдалих спроб WebSocket.

HTTP 401 або 403 для офлайн-форми

DNS указує на приватну адресу

Перевірка повідомляє про непублічну адресу, якщо хоча б одна адреса імені приватна, локальна або зарезервована (наприклад 10.0.0.0/8, 192.168.0.0/16, 100.64.0.0/10 або fc00::/7). Типові причини — запис із внутрішньої DNS або внутрішній балансувальник навантаження. Відвідувачі також не можуть досягти таких адрес; публічний запис має вказувати на публічну адресу.

Перевірка вручну

Ви можете самостійно звернутися до адреси перевірки з кроку 3:

curl -s https://chat.your-domain.com/rc_wss/eigene-domain-probe

За правильного налаштування відповідь така:

{"rc":"eigene-domain-probe","v":1,"schluessel":"gueltig","client_ip":"gesetzt","site":"SITE_KEY","host":"chat.your-domain.com"}

Значення — німецькі слова: schluessel (ключ) має значення gueltig (дійсний), unbekannt (невідомий) або fehlt (відсутній); client_ip — gesetzt (задано), fehlt (відсутній) або ungueltig (недійсний). site і host включено лише з дійсним ключем. Відповідь ніколи не містить адреси.

Докладніше в документації

Докладно: Власний домен і проксі першої сторони. Усі розділи є в документації.

Оновлено: