Kako Vam možemo pomoći?

Ukratko nam recite o čemu je riječ – odgovorit ćemo Vam e-poštom.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Na početnu stranicu

Domínio próprio (first-party)

Forneça widget, conexão do chat e formulário offline por um subdomínio do seu site: requisitos, configuração, modelos para nginx, Apache e Caddy e solução de problemas.

Com domínio próprio, o chat passa por um subdomínio do seu site, por exemplo chat.your-domain.com. Seu servidor aceita as requisições do widget e as encaminha a reactive.chat como proxy reverso. Esta página é destinada a quem administra esse servidor.

O que o domínio próprio faz

Passam pelo seu subdomínio:

Com isso:

O que ele não é

Requisitos

Atrás de um balanceador de carga ou CDN, seu proxy vê primeiro o endereço desse equipamento, não o do visitante. Nesse caso, configure a obtenção do endereço real: no nginx, módulo real_ip (set_real_ip_from, real_ip_header); no Apache, mod_remoteip (RemoteIPHeader, RemoteIPTrustedProxy); no Caddy, opção trusted_proxies e marcador {client_ip} no lugar de {remote_host}. Nossa verificação não detecta esse caso: só vê que há um endereço válido, não qual é.

Configuração passo a passo

  1. Abrir a página. Abra o projeto na conta. Na seção “Incorporação”, você encontra o link Configurar seu domínio próprio.
  2. Inserir o host. Em “1. Host”, insira o nome, por exemplo chat.your-domain.com, e salve.
  3. Criar a chave. Em “2. Chave de proxy”, clique em Gerar chave. A chave começa com rcpx_ e é mostrada exatamente uma vez; armazenamos apenas um hash. Copie-a imediatamente. É secreta e só deve ficar na configuração do proxy, nunca em uma página, script de navegador ou repositório.
  4. Inserir o modelo. Em “3. Configuração do servidor”, há modelos prontos para nginx, Apache e Caddy. Logo após criar a chave, eles já a contêm; depois mostram __RC_PROXY_KEY__, que você substitui pela chave. Ajuste os caminhos dos certificados se necessário, teste a configuração (nginx -t ou apachectl configtest) e recarregue o servidor.
  5. Verificar. Em “5. Verificação”, clique em Verificar agora. Acessamos seu subdomínio de fora e verificamos cinco rotas separadamente (veja abaixo). Nenhuma mensagem é criada. Uma nova verificação só é possível após 10 segundos.
  6. Substituir o código. Só quando as cinco etapas mostrarem “OK”, substitua o código de incorporação atual no site pelo de “4. Código de incorporação”.

O novo código fica assim; os dois endereços apontam para seu subdomínio:

<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>

O widget obtém a localização dos arquivos de src e a conexão do chat de data-rc-ws-url, e deriva dela o endereço do formulário offline. Outros atributos, como data-rc-locale, permanecem iguais; estão descritos em Incorporar o widget. O código da página da conta também contém a cor da marca.

As cinco etapas da verificação

Cada etapa mostra “OK”, “Erro”, “Aviso” ou “não verificado”. A verificação só passa quando todas mostram “OK”; um aviso não conta como aprovação. As verificações usam apenas HTTPS na porta 443, não seguem redirecionamentos e têm 5 segundos por etapa.

  1. Resolução de nome. O nome precisa de um registro DNS, e todos os endereços devem ser públicos. É informado se não houver registro A nem AAAA ou se pelo menos um endereço for privado, local ou reservado; não verificamos esses endereços. Todas as etapas seguintes se conectam exatamente ao endereço verificado aqui.
  2. loader.js pelo seu domínio. Carregamos https://chat.your-domain.com/loader.js e comparamos o arquivo com o nosso. Se não houver conexão, resposta ou certificado válido, as etapas restantes são puladas (“não verificado”). Se o proxy responder com redirecionamento em vez do arquivo, é um erro. Se o arquivo diferir do nosso, a causa costuma ser um cache no proxy ou um destino errado para /.
  3. Chave e endereço do visitante. Chamamos um endereço de verificação sob /rc_wss/. Nosso servidor responde se recebeu chave e endereço de visitante válidos e a qual projeto a chave pertence. Erros possíveis: cabeçalho X-RC-Proxy-Key ausente; chave desconhecida (erro de digitação, desatualizada ou removida; logo após criá-la, pode levar até 30 segundos); cabeçalho X-RC-Client-IP ausente ou sem endereço válido; chave de outro projeto. Um aviso aparece se a chave for válida, mas estiver registrada para outro host.
  4. Conexão do chat (WebSocket). Abrimos uma conexão WebSocket por wss://chat.your-domain.com/rc_wss/ e esperamos HTTP 101 com protocolo rc.v1. “Sem upgrade de WebSocket” significa que o proxy não repassa os cabeçalhos Upgrade e Connection.
  5. Formulário offline. Chamamos https://chat.your-domain.com/api/offline/SITE_KEY com GET. Nosso endpoint responde HTTP 405 porque só aceita POST; isso confirma que a requisição chega, sem criar mensagem. Outros resultados: origem rejeitada (aviso, veja a solução de problemas), projeto inativo, destino exige autenticação (HTTP 401).

As etapas 2 a 5 também podem informar HTTP 421 (cabeçalho Host, veja a solução de problemas). Se a etapa 3 ou 5 disser “A resposta não vem de reactive.chat”, esse caminho aponta para um destino errado no proxy.

O que o proxy deve encaminhar

Nas três rotas:

Para /rc_wss/ e /api/offline/, o proxy também define dois cabeçalhos em cada requisição:

“Define” significa que tudo o que o navegador enviar com esses nomes é sobrescrito e nunca chega até nós. Na rota para a rede de distribuição, os dois cabeçalhos são limpos; a chave só vai para o servidor do chat.

Fallback HTTP pelo seu domínio

Se a rede do visitante não permite WebSocket, o widget muda automaticamente para o fallback HTTP. Usa o mesmo caminho /rc_wss/ e não precisa de entrada adicional no proxy. Duas coisas são necessárias:

Os modelos

Os modelos abaixo correspondem aos da página da conta. A página já preenche seu host, os endereços de destino válidos para sua conta e, logo após criar a chave, a própria chave. Por isso, o melhor é copiar o modelo de lá; as versões aqui servem de referência. Os marcadores são chat.your-domain.com, SITE_KEY e __RC_PROXY_KEY__.

nginx

Deve ficar no contexto http, por exemplo como /etc/nginx/conf.d/chat.your-domain.com.conf. No RHEL e CentOS, o pacote de CAs fica em /etc/pki/tls/certs/ca-bundle.crt, não em /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

O modelo contém apenas o VirtualHost da porta 443. Configure o redirecionamento da porta 80 e o certificado como nos outros hosts. SSLProxyVerifyDepth 5 é obrigatório: sem ele, o Apache verifica a cadeia de certificados do destino até apenas um nível, e a conexão falha com “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 obtém o certificado sozinho; WebSockets não precisam de configuração extra. header_up com um valor substitui o que o navegador enviou com esse nome.

# 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
            }
        }
    }
}

A chave de proxy

Solução de problemas

HTTP 421 (Misdirected Request)

Nosso servidor rejeita o cabeçalho Host: seu proxy envia o próprio nome em vez do nome do destino. Host e SNI precisam usar o nome do destino: no nginx, proxy_set_header Host mais proxy_ssl_server_name on com proxy_ssl_name; no Apache, ProxyPreserveHost Off; no Caddy, header_up Host e tls_server_name.

HTTP 502 ou erros SSL na conexão com o destino

O proxy não consegue estabelecer a conexão TLS conosco ou não confia no certificado.

HTTP 413 ao enviar anexos

Os uploads passam por /rc_wss/. Sem configuração própria, nginx aceita apenas 1 MB; por isso o modelo define client_max_body_size 32m na seção /rc_wss/. Se houver um balanceador de carga ou CDN na frente do proxy, precisa de pelo menos o mesmo limite.

Conexão do chat sem upgrade

A verificação informa “Sem upgrade de WebSocket” com o status HTTP recebido no lugar. O chat continua pelo fallback HTTP, mas só se conecta após duas tentativas WebSocket com falha.

HTTP 401 ou 403 no formulário offline

DNS aponta para um endereço privado

A verificação informa um endereço não público se pelo menos um endereço do nome for privado, local ou reservado (por exemplo 10.0.0.0/8, 192.168.0.0/16, 100.64.0.0/10 ou fc00::/7). Causas comuns são um registro de DNS interno ou balanceador de carga interno. Seus visitantes também não conseguem acessar esses endereços; o registro público precisa apontar para um endereço público.

Verificar manualmente

Você pode chamar o endereço de verificação da etapa 3:

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

Com uma configuração correta, a resposta é:

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

Os valores são palavras em alemão: schluessel (chave) é gueltig (válida), unbekannt (desconhecida) ou fehlt (ausente); client_ip é gesetzt (definido), fehlt (ausente) ou ungueltig (inválido). site e host só são incluídos com uma chave válida. A resposta nunca contém um endereço.

Saiba mais na documentação

Em detalhes: Domínio próprio e proxy first-party. Todos os capítulos estão na documentação.

Zadnje ažuriranje: