Jak możemy pomóc?

Napisz krótko, o co chodzi - odpowiemy mailowo.

Ta strona jest chroniona przez reCAPTCHA. Obowiązują Polityka prywatności oraz Warunki korzystania z usług Google.
Strona główna

Własna domena (first-party)

Widżet, połączenie czatu i formularz offline z subdomeny Twojej strony – wymagania, konfiguracja, szablony dla nginx, Apache i Caddy, rozwiązywanie problemów.

Z własną domeną czat działa przez subdomenę Twojej strony, na przykład chat.twoja-domena.pl. Twój serwer przyjmuje żądania widżetu i jako reverse proxy przekazuje je do reactive.chat. Ta strona jest przeznaczona dla osoby, która prowadzi ten serwer.

Co daje własna domena

Przez Twoją subdomenę przechodzą:

W efekcie:

Czym nie jest

Wymagania

Za load balancerem lub CDN Twoje proxy widzi najpierw adres tego urządzenia zamiast adresu odwiedzającego. W takim przypadku skonfiguruj przejmowanie prawdziwego adresu odwiedzającego: w nginx moduł real_ip (set_real_ip_from, real_ip_header), w Apache mod_remoteip (RemoteIPHeader, RemoteIPTrustedProxy), w Caddy opcję trusted_proxies i placeholder {client_ip} zamiast {remote_host}. Nasz test nie wykryje tego przypadku: widzi tylko, że ustawiono prawidłowy adres, a nie jaki.

Konfiguracja krok po kroku

  1. Otwórz stronę. Otwórz projekt w swoim koncie. W sekcji „Osadzenie” znajdziesz link Skonfiguruj własną domenę.
  2. Wpisz host. W „1. Host” wpisz nazwę, na przykład chat.twoja-domena.pl, i zapisz.
  3. Utwórz klucz. W „2. Klucz proxy” kliknij Utwórz klucz. Klucz zaczyna się od rcpx_ i jest pokazywany dokładnie raz; przechowujemy tylko jego skrót. Skopiuj go od razu. Jest tajny i należy wyłącznie do konfiguracji Twojego proxy – nigdy do strony internetowej, skryptu w przeglądarce ani repozytorium.
  4. Wstaw szablon. W „3. Konfiguracja serwera” są gotowe szablony dla nginx, Apache i Caddy. Zaraz po utworzeniu klucza już go zawierają; później widnieje w nich __RC_PROXY_KEY__, który zastępujesz kluczem. W razie potrzeby dostosuj ścieżki certyfikatów, sprawdź konfigurację (nginx -t lub apachectl configtest) i przeładuj serwer.
  5. Sprawdź. W „5. Sprawdź” kliknij Sprawdź teraz. Wywołujemy Twoją subdomenę z zewnątrz i sprawdzamy pięć dróg osobno (patrz niżej). Nie powstaje przy tym żadna wiadomość. Kolejny test jest możliwy najwcześniej po 10 sekundach.
  6. Wymień fragment kodu. Dopiero gdy wszystkie pięć kroków pokazuje „w porządku”, zastąp dotychczasowy fragment kodu na swojej stronie tym z „4. Fragment kodu”.

Nowy fragment wygląda tak – oba adresy wskazują na Twoją subdomenę:

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

Z src widżet bierze miejsce swoich plików, z data-rc-ws-url połączenie czatu – i na tej podstawie adres formularza offline. Pozostałe atrybuty, takie jak data-rc-locale, zostają bez zmian; opisuje je strona Osadzenie widgetu. Fragment na stronie konta zawiera dodatkowo kolor Twojej marki.

Pięć kroków testu

Każdy krok pokazuje „w porządku”, „błąd”, „uwaga” lub „nie sprawdzono”. Test jest zaliczony tylko wtedy, gdy wszystkie pięć pokazuje „w porządku”; uwaga nie liczy się jako zaliczenie. Sprawdzamy wyłącznie przez HTTPS na porcie 443, nie podążamy za przekierowaniami, każdy krok ma 5 sekund.

  1. Rozwiązywanie nazwy. Nazwa musi mieć rekord DNS, a wszystkie jej adresy muszą być publiczne. Błąd pojawia się, gdy nie ma ani rekordu A, ani AAAA, albo gdy co najmniej jeden adres jest prywatny, lokalny lub zarezerwowany – takich adresów nie sprawdzamy. Wszystkie kolejne kroki łączą się dokładnie z adresem sprawdzonym tutaj.
  2. loader.js przez Twoją domenę. Pobieramy https://chat.twoja-domena.pl/loader.js i porównujemy plik z naszym. Jeśli nie dojdzie do połączenia, nie przyjdzie odpowiedź albo brakuje ważnego certyfikatu, pozostałe kroki są pomijane („nie sprawdzono”). Jeśli proxy odpowiada przekierowaniem zamiast pliku, to błąd. Jeśli plik różni się od naszego, przyczyną jest zwykle pamięć podręczna w proxy albo zły cel dla /.
  3. Klucz i adres odwiedzającego. Wywołujemy adres testowy pod /rc_wss/. Nasz serwer odpowiada, czy otrzymał prawidłowy klucz i prawidłowy adres odwiedzającego oraz do którego projektu należy klucz. Możliwe błędy: brak nagłówka X-RC-Proxy-Key; nieznany klucz (literówka, nieaktualny lub usunięty – zaraz po utworzeniu może to potrwać do 30 sekund); brak nagłówka X-RC-Client-IP lub brak w nim prawidłowego adresu; klucz należy do innego projektu. Uwaga pojawia się, gdy klucz jest ważny, ale zarejestrowany dla innego hosta.
  4. Połączenie czatu (WebSocket). Nawiązujemy połączenie WebSocket przez wss://chat.twoja-domena.pl/rc_wss/ i oczekujemy HTTP 101 z protokołem rc.v1. „Brak upgrade WebSocket” oznacza: proxy nie przekazuje nagłówków Upgrade i Connection.
  5. Formularz offline. Wywołujemy https://chat.twoja-domena.pl/api/offline/SITE_KEY metodą GET. Nasz punkt końcowy odpowiada HTTP 405, bo przyjmuje tylko POST – po tym poznajemy, że żądanie do nas dociera, a żadna wiadomość nie powstaje. Inne wyniki: odrzucone pochodzenie (uwaga, patrz rozwiązywanie problemów), projekt nieaktywny, cel wymaga uwierzytelnienia (HTTP 401).

Kroki 2–5 mogą też zgłosić HTTP 421 (nagłówek Host, patrz rozwiązywanie problemów). Jeśli krok 3 lub 5 zgłasza „Odpowiedź nie pochodzi od reactive.chat”, ta ścieżka w proxy wskazuje na zły cel.

Co proxy musi przekazywać

Dla wszystkich trzech dróg:

Dla /rc_wss/ i /api/offline/ proxy dodatkowo ustawia przy każdym żądaniu dwa nagłówki:

„Ustawia” oznacza: to, co przeglądarka wyśle pod tymi nazwami, zostaje nadpisane i nigdy do nas nie dociera. Na drodze do sieci dostarczania oba nagłówki są czyszczone; klucz trafia tylko do serwera czatu.

Tryb awaryjny HTTP przez Twoją domenę

Jeśli sieć odwiedzającego nie przepuszcza WebSocket, widżet automatycznie przechodzi na tryb awaryjny HTTP. Korzysta on z tej samej ścieżki /rc_wss/ i nie wymaga dodatkowego wpisu w proxy. Potrzebne są dwie rzeczy:

Szablony

Poniższe szablony odpowiadają tym ze strony konta. Strona konta wstawia już Twój host, adresy docelowe właściwe dla Twojego konta oraz – zaraz po utworzeniu – Twój klucz. Dlatego najlepiej kopiuj szablon stamtąd; wersje tutaj służą do wglądu. Placeholdery to chat.twoja-domena.pl, SITE_KEY i __RC_PROXY_KEY__.

nginx

Należy do kontekstu http, na przykład jako /etc/nginx/conf.d/chat.twoja-domena.pl.conf. W RHEL i CentOS pakiet CA leży w /etc/pki/tls/certs/ca-bundle.crt zamiast /etc/ssl/certs/ca-certificates.crt.

# reactive.chat - own domain (first-party proxy) for site key SITE_KEY
# Host: chat.twoja-domena.pl
#
# Needs nginx >= 1.7 with the ssl module. Put this file into the http
# context (e.g. /etc/nginx/conf.d/chat.twoja-domena.pl.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.twoja-domena.pl;
    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.twoja-domena.pl;

    ssl_certificate     /etc/letsencrypt/live/chat.twoja-domena.pl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/chat.twoja-domena.pl/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

Szablon zawiera tylko VirtualHost dla portu 443. Przekierowanie z portu 80 i wystawienie certyfikatu skonfiguruj tak jak dla pozostałych hostów. SSLProxyVerifyDepth 5 jest konieczne: bez tej linii Apache sprawdza łańcuch certyfikatów celu tylko na jeden poziom w głąb, a połączenie kończy się błędem „Certificate Chain too long”.

# reactive.chat - own domain (first-party proxy) for site key SITE_KEY
# Host: chat.twoja-domena.pl
#
# 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.twoja-domena.pl

    # 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.twoja-domena.pl_access.log combined env=!rc_no_log

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/chat.twoja-domena.pl/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/chat.twoja-domena.pl/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 pobiera certyfikat sam, WebSockety nie wymagają osobnego ustawienia. header_up z wartością zastępuje to, co przeglądarka wysłała pod tą nazwą.

# reactive.chat - own domain (first-party proxy) for site key SITE_KEY
# Host: chat.twoja-domena.pl
#
# 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.twoja-domena.pl {
    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
            }
        }
    }
}

Klucz proxy

Rozwiązywanie problemów

HTTP 421 (Misdirected Request)

Nasz serwer odrzuca nagłówek Host: proxy wysyła Twoją własną nazwę zamiast nazwy celu. Nagłówek Host i SNI muszą być nazwą celu – w nginx proxy_set_header Host oraz proxy_ssl_server_name on z proxy_ssl_name, w Apache ProxyPreserveHost Off, w Caddy header_up Host i tls_server_name.

HTTP 502 lub błędy SSL w drodze do celu

Proxy nie może nawiązać połączenia TLS z nami albo nie ufa certyfikatowi.

HTTP 413 przy przesyłaniu załączników

Przesyłanie plików idzie przez /rc_wss/. Bez własnego ustawienia nginx przyjmuje tylko 1 MB; dlatego szablon ustawia client_max_body_size 32m w sekcji dla /rc_wss/. Jeśli przed Twoim proxy stoi load balancer lub CDN, potrzebuje co najmniej tego samego limitu.

Połączenie czatu bez upgrade

Test zgłasza „Brak upgrade WebSocket” wraz ze statusem HTTP, który otrzymał zamiast tego. Czat działa wtedy dalej w trybie awaryjnym HTTP, ale łączy się dopiero po dwóch nieudanych próbach WebSocket.

HTTP 401 lub 403 przy formularzu offline

DNS wskazuje na adres prywatny

Test zgłasza adres niepubliczny, jeśli co najmniej jeden adres nazwy jest prywatny, lokalny lub zarezerwowany (na przykład 10.0.0.0/8, 192.168.0.0/16, 100.64.0.0/10 lub fc00::/7). Częste przyczyny to rekord z wewnętrznego DNS albo wewnętrzny load balancer. Twoi odwiedzający też nie osiągną takich adresów; publiczny rekord musi wskazywać na adres publiczny.

Sprawdzenie ręczne

Adres testowy z kroku 3 możesz wywołać sam:

curl -s https://chat.twoja-domena.pl/rc_wss/eigene-domain-probe

Przy prawidłowej konfiguracji odpowiedź brzmi:

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

Wartości są niemieckimi słowami: schluessel (klucz) to gueltig (ważny), unbekannt (nieznany) lub fehlt (brak); client_ip to gesetzt (ustawiony), fehlt (brak) lub ungueltig (nieprawidłowy). site i host pojawiają się tylko przy ważnym kluczu. Odpowiedź nigdy nie zawiera adresu.

Stan na: