Hvordan kan vi hjælpe?

Fortæl os kort, hvad det drejer sig om – vi vender tilbage på e-mail.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Til startsiden

Dominio personalizzato (first-party)

Distribuire widget, connessione della chat e modulo offline da un sottodominio del Suo sito: requisiti, configurazione, modelli per nginx, Apache e Caddy e risoluzione dei problemi.

Con un dominio personalizzato, la chat passa attraverso un sottodominio del Suo sito, ad esempio chat.your-domain.com. Il Suo server accetta le richieste del widget e le inoltra a reactive.chat come proxy inverso. Questa pagina è destinata a chi gestisce quel server.

A che cosa serve un dominio personalizzato

Passano attraverso il Suo sottodominio:

Di conseguenza:

Che cosa non è

Requisiti

Dietro un bilanciatore di carico o una CDN, il proxy vede inizialmente l’indirizzo di quel dispositivo anziché quello del visitatore. Configuri quindi l’acquisizione dell’indirizzo reale: in nginx il modulo real_ip (set_real_ip_from, real_ip_header), in Apache mod_remoteip (RemoteIPHeader, RemoteIPTrustedProxy), in Caddy l’opzione trusted_proxies e il segnaposto {client_ip} al posto di {remote_host}. La nostra verifica non può rilevare questo caso: vede solo che è impostato un indirizzo valido, non quale.

Configurazione passo dopo passo

  1. Aprire la pagina. Apra il progetto nell’account. Nella sezione «Incorporamento» trova il link Configura un dominio personalizzato.
  2. Inserire l’host. In «1. Host», inserisca il nome, ad esempio chat.your-domain.com, e salvi.
  3. Creare la chiave. In «2. Chiave del proxy», faccia clic su Genera chiave. La chiave inizia con rcpx_ e viene mostrata una sola volta; ne conserviamo solo un hash. La copi subito. È segreta e deve trovarsi solo nella configurazione del proxy, mai in una pagina web, uno script del browser o un repository.
  4. Inserire il modello. In «3. Configurazione del server» trova modelli pronti per nginx, Apache e Caddy. Subito dopo la creazione contengono già la chiave; in seguito mostrano __RC_PROXY_KEY__, da sostituire con la chiave. Se necessario adatti i percorsi dei certificati, verifichi la configurazione (nginx -t o apachectl configtest) e ricarichi il server.
  5. Verificare. In «5. Verifica», faccia clic su Verifica ora. Chiamiamo il sottodominio dall’esterno e verifichiamo separatamente cinque percorsi (vedere sotto). Non viene creato alcun messaggio. Una nuova verifica è possibile non prima di 10 secondi.
  6. Sostituire il codice. Solo quando tutti e cinque i passi mostrano «OK», sostituisca il codice attuale sul sito con quello di «4. Codice di incorporamento».

Il nuovo codice appare così: entrambi gli indirizzi puntano al Suo sottodominio:

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

Il widget ricava la posizione dei file da src e la connessione della chat da data-rc-ws-url; da quest’ultima deriva l’indirizzo del modulo offline. Gli altri attributi, come data-rc-locale, restano invariati; sono descritti in Incorporare il widget. Il codice nella pagina dell’account contiene anche il colore del marchio.

I cinque passi della verifica

Ogni passo mostra «OK», «Errore», «Nota» o «non verificato». La verifica è superata solo se tutti e cinque mostrano «OK»; una nota non conta come superamento. Le verifiche usano solo HTTPS sulla porta 443, non seguono reindirizzamenti e ogni passo dispone di 5 secondi.

  1. Risoluzione del nome. Il nome deve avere un record DNS e tutti i suoi indirizzi devono essere pubblici. Viene segnalata l’assenza sia di record A sia di record AAAA, oppure la presenza di almeno un indirizzo privato, locale o riservato: questi indirizzi non vengono verificati. Tutti i passi successivi si collegano esattamente all’indirizzo verificato qui.
  2. loader.js tramite il Suo dominio. Carichiamo https://chat.your-domain.com/loader.js e confrontiamo il file con il nostro. Se non si ottengono connessione, risposta o certificato valido, i passi rimanenti vengono saltati («non verificato»). Se il proxy risponde con un reindirizzamento anziché il file, è un errore. Se il file differisce dal nostro, di solito la causa è una cache nel proxy o una destinazione errata per /.
  3. Chiave e indirizzo del visitatore. Chiamiamo un indirizzo di verifica sotto /rc_wss/. Il nostro server indica se ha ricevuto una chiave valida e un indirizzo del visitatore valido e a quale progetto appartiene la chiave. Possibili errori: manca l’intestazione X-RC-Proxy-Key; la chiave è sconosciuta (errata, precedente o rimossa; subito dopo la creazione possono servire fino a 30 secondi); manca X-RC-Client-IP o non contiene un indirizzo valido; la chiave appartiene a un altro progetto. Compare una nota se la chiave è valida ma registrata per un host diverso.
  4. Connessione della chat (WebSocket). Apriamo una connessione WebSocket tramite wss://chat.your-domain.com/rc_wss/ e attendiamo HTTP 101 con protocollo rc.v1. «Nessun upgrade WebSocket» significa che il proxy non inoltra le intestazioni Upgrade e Connection.
  5. Modulo offline. Chiamiamo https://chat.your-domain.com/api/offline/SITE_KEY con GET. Il nostro endpoint risponde con HTTP 405 perché accetta solo POST: questo conferma che la richiesta ci raggiunge senza creare un messaggio. Altri risultati: origine rifiutata (una nota, vedere la risoluzione dei problemi), progetto non attivo, destinazione che richiede autenticazione (HTTP 401).

I passi da 2 a 5 possono anche segnalare HTTP 421 (intestazione Host, vedere la risoluzione dei problemi). Se il passo 3 o 5 indica «La risposta non proviene da reactive.chat», quel percorso punta a una destinazione errata nel proxy.

Che cosa deve inoltrare il proxy

Per tutti e tre i percorsi:

Per /rc_wss/ e /api/offline/ il proxy imposta inoltre due intestazioni su ogni richiesta:

«Imposta» significa che ciò che un browser invia con questi nomi viene sovrascritto e non ci raggiunge mai. Sul percorso verso la rete di distribuzione entrambe le intestazioni vengono rimosse; la chiave va solo al server della chat.

Fallback HTTP tramite il Suo dominio

Se la rete di un visitatore non lascia passare un WebSocket, il widget passa automaticamente al fallback HTTP. Usa lo stesso percorso /rc_wss/ e non richiede una voce aggiuntiva nel proxy. Servono due condizioni:

I modelli

I modelli seguenti corrispondono a quelli nella pagina dell’account. L’account inserisce già host, indirizzi di destinazione validi per il Suo account e, subito dopo la creazione, la chiave. È quindi preferibile copiare il modello da lì; le versioni qui riportate servono da riferimento. I segnaposto sono chat.your-domain.com, SITE_KEY e __RC_PROXY_KEY__.

nginx

Va inserito nel contesto http, ad esempio come /etc/nginx/conf.d/chat.your-domain.com.conf. Su RHEL e CentOS il bundle CA si trova in /etc/pki/tls/certs/ca-bundle.crt anziché in /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

Il modello contiene solo il VirtualHost per la porta 443. Configuri il reindirizzamento dalla porta 80 e il certificato come per gli altri host. SSLProxyVerifyDepth 5 è necessario: senza, Apache verifica solo un livello della catena del certificato della destinazione e la connessione fallisce con «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 ottiene il certificato autonomamente; i WebSocket non richiedono impostazioni aggiuntive. header_up con un valore sostituisce ciò che il browser ha inviato con quel 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
            }
        }
    }
}

La chiave del proxy

Risoluzione dei problemi

HTTP 421 (Misdirected Request)

Il nostro server rifiuta l’intestazione Host: il proxy invia il Suo nome anziché quello della destinazione. Host e SNI devono corrispondere al nome della destinazione: in nginx proxy_set_header Host insieme a proxy_ssl_server_name on con proxy_ssl_name, in Apache ProxyPreserveHost Off, in Caddy header_up Host e tls_server_name.

HTTP 502 o errori SSL verso la destinazione

Il proxy non riesce a stabilire la connessione TLS verso di noi oppure non considera attendibile il certificato.

HTTP 413 durante il caricamento degli allegati

I caricamenti passano per /rc_wss/. Senza un’impostazione specifica nginx accetta solo 1 MB; il modello imposta quindi client_max_body_size 32m nella sezione /rc_wss/. Se davanti al proxy si trova un bilanciatore di carico o una CDN, deve avere almeno lo stesso limite.

Connessione della chat senza upgrade

La verifica segnala «Nessun upgrade WebSocket» con lo stato HTTP ricevuto al suo posto. La chat continua tramite fallback HTTP, ma si collega solo dopo due tentativi WebSocket falliti.

HTTP 401 o 403 sul modulo offline

Il DNS punta a un indirizzo privato

La verifica segnala un indirizzo non pubblico se almeno un indirizzo del nome è privato, locale o riservato (ad esempio 10.0.0.0/8, 192.168.0.0/16, 100.64.0.0/10 o fc00::/7). Cause comuni sono un record del DNS interno o un bilanciatore interno. Neanche i visitatori possono raggiungere tali indirizzi; il record pubblico deve puntare a un indirizzo pubblico.

Verificare manualmente

Può chiamare personalmente l’indirizzo di verifica del passo 3:

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

Con una configurazione corretta, la risposta è:

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

I valori sono parole tedesche: schluessel (chiave) è gueltig (valida), unbekannt (sconosciuta) o fehlt (mancante); client_ip è gesetzt (impostato), fehlt (mancante) o ungueltig (non valido). site e host compaiono solo con una chiave valida. La risposta non contiene mai un indirizzo.

Approfondimenti nella documentazione

In dettaglio: Dominio personalizzato e proxy first-party. Tutti i capitoli sono nella documentazione.

Senest opdateret: