איך אפשר לעזור?

אפשר לתאר בקצרה במה מדובר, ונחזור בדוא״ל.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
לעמוד הבית

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.

עדכון אחרון: