Wie können wir helfen?

Schreib uns kurz, worum es geht - wir melden uns per E-Mail.

Diese Seite ist durch reCAPTCHA geschützt. Es gelten die Datenschutzerklärung und die Nutzungsbedingungen von Google.
Zur Startseite

Eigene Domain (First-Party)

Widget, Chat-Verbindung und Offline-Formular über eine Subdomain Ihrer Website ausliefern – Voraussetzungen, Einrichtung, Vorlagen für nginx, Apache und Caddy, Fehlerbehebung.

Mit einer eigenen Domain läuft der Chat über eine Subdomain Ihrer Website, zum Beispiel chat.ihre-domain.de. Ihr Server nimmt die Anfragen des Widgets an und reicht sie als Reverse-Proxy an reactive.chat weiter. Diese Seite richtet sich an die Person, die diesen Server betreibt.

Was die eigene Domain leistet

Über Ihre Subdomain laufen:

Daraus folgt:

Was sie nicht ist

Voraussetzungen

Hinter einem Load-Balancer oder CDN sieht Ihr Proxy zunächst dessen Adresse statt der des Besuchers. Richten Sie in diesem Fall die Übernahme der echten Besucheradresse ein: bei nginx das Modul real_ip (set_real_ip_from, real_ip_header), bei Apache mod_remoteip (RemoteIPHeader, RemoteIPTrustedProxy), bei Caddy die Option trusted_proxies und den Platzhalter {client_ip} statt {remote_host}. Unsere Prüfung kann diesen Fall nicht erkennen: Sie sieht nur, dass eine gültige Adresse gesetzt ist, nicht welche.

Einrichtung Schritt für Schritt

  1. Seite öffnen. Öffnen Sie im Konto das Projekt. Im Abschnitt „Einbindung“ steht der Link Eigene Domain einrichten.
  2. Host eintragen. Tragen Sie unter „1. Host“ den Namen ein, zum Beispiel chat.ihre-domain.de, und speichern Sie.
  3. Schlüssel erzeugen. Klicken Sie unter „2. Proxy-Schlüssel“ auf Schlüssel erzeugen. Der Schlüssel beginnt mit rcpx_ und wird genau einmal angezeigt; wir speichern nur einen Hash davon. Kopieren Sie ihn sofort. Er ist geheim und gehört ausschließlich in die Konfiguration Ihres Proxys – nie in eine Webseite, ein Skript im Browser oder ein Repository.
  4. Vorlage einsetzen. Unter „3. Server-Konfiguration“ stehen fertige Vorlagen für nginx, Apache und Caddy. Direkt nach dem Erzeugen enthalten sie Ihren Schlüssel bereits; später steht dort __RC_PROXY_KEY__, den Sie durch den Schlüssel ersetzen. Passen Sie bei Bedarf die Zertifikatspfade an, prüfen Sie die Konfiguration (nginx -t bzw. apachectl configtest) und laden Sie den Server neu.
  5. Prüfen. Klicken Sie unter „5. Prüfen“ auf Jetzt prüfen. Wir rufen Ihre Subdomain von außen auf und prüfen fünf Wege einzeln (siehe unten). Dabei entsteht keine Nachricht. Eine neue Prüfung ist frühestens nach 10 Sekunden möglich.
  6. Schnipsel austauschen. Erst wenn alle fünf Schritte „in Ordnung“ zeigen, ersetzen Sie den bisherigen Einbau-Schnipsel auf Ihrer Website durch den aus „4. Einbau-Schnipsel“.

Der neue Schnipsel sieht so aus – beide Adressen zeigen auf Ihre Subdomain:

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

Aus src nimmt das Widget den Ort seiner Dateien, aus data-rc-ws-url die Chat-Verbindung und davon abgeleitet die Adresse des Offline-Formulars. Weitere Angaben wie data-rc-locale bleiben, wie sie sind; sie sind unter Das Widget einbinden beschrieben. Der Schnipsel auf der Kontoseite trägt zusätzlich Ihre Markenfarbe.

Die fünf Prüfschritte

Jeder Schritt zeigt „in Ordnung“, „Fehler“, „Hinweis“ oder „nicht geprüft“. Bestanden ist die Prüfung nur, wenn alle fünf „in Ordnung“ zeigen; ein Hinweis zählt nicht als bestanden. Geprüft wird nur über HTTPS auf Port 443, Umleitungen werden nicht verfolgt, jeder Schritt hat 5 Sekunden Zeit.

  1. Namensauflösung. Der Name muss einen DNS-Eintrag haben, und alle seine Adressen müssen öffentlich sein. Gemeldet wird, wenn weder ein A- noch ein AAAA-Eintrag gefunden wird oder wenn mindestens eine Adresse privat, lokal oder reserviert ist – solche Adressen prüfen wir nicht. Alle folgenden Schritte verbinden sich mit genau der hier geprüften Adresse.
  2. loader.js über Ihre Domain. Wir laden https://chat.ihre-domain.de/loader.js und vergleichen die Datei mit unserer. Kommt keine Verbindung, keine Antwort oder kein gültiges Zertifikat zustande, werden die übrigen Schritte übersprungen („nicht geprüft“). Antwortet der Proxy mit einer Umleitung statt mit der Datei, ist das ein Fehler. Weicht die Datei von unserer ab, liegt das meist an einem Zwischenspeicher im Proxy oder an einem falschen Ziel für /.
  3. Schlüssel und Besucheradresse. Wir rufen eine Prüfadresse unter /rc_wss/ auf. Unser Server antwortet darauf, ob er einen gültigen Schlüssel und eine gültige Besucheradresse bekommen hat und zu welchem Projekt der Schlüssel gehört. Mögliche Fehler: Kopfzeile X-RC-Proxy-Key fehlt; Schlüssel unbekannt (vertippt, veraltet oder entfernt – direkt nach dem Erzeugen kann es bis zu 30 Sekunden dauern); Kopfzeile X-RC-Client-IP fehlt oder enthält keine gültige Adresse; Schlüssel gehört zu einem anderen Projekt. Ein Hinweis erscheint, wenn der Schlüssel gültig, aber für einen anderen Host eingetragen ist.
  4. Chat-Verbindung (WebSocket). Wir bauen eine WebSocket-Verbindung über wss://chat.ihre-domain.de/rc_wss/ auf und erwarten HTTP 101 mit dem Protokoll rc.v1. „Kein WebSocket-Upgrade“ heißt: der Proxy reicht die Kopfzeilen Upgrade und Connection nicht weiter.
  5. Offline-Formular. Wir rufen https://chat.ihre-domain.de/api/offline/SITE_KEY mit GET auf. Unser Endpunkt antwortet darauf mit HTTP 405, weil er nur POST annimmt – daran erkennen wir, dass die Anfrage bei uns ankommt, ohne dass eine Nachricht entsteht. Weitere Ergebnisse: Herkunft abgewiesen (Hinweis, siehe Fehlerbehebung), Projekt nicht aktiv, Ziel verlangt eine Anmeldung (HTTP 401).

In den Schritten 2 bis 5 kann außerdem HTTP 421 gemeldet werden (Host-Kopf, siehe Fehlerbehebung). Meldet Schritt 3 oder 5 „Die Antwort kommt nicht von reactive.chat“, zeigt dieser Pfad im Proxy auf ein falsches Ziel.

Was der Proxy weiterleiten muss

Für alle drei Wege gilt:

Für /rc_wss/ und /api/offline/ setzt der Proxy zusätzlich bei jeder Anfrage zwei Kopfzeilen:

„Setzen“ heißt: Was ein Browser unter diesen Namen mitschickt, wird überschrieben und erreicht uns nie. Auf dem Weg zum Auslieferungsnetz werden beide Kopfzeilen geleert; der Schlüssel geht nur an den Chat-Server.

HTTP-Rückfall über Ihre Domain

Lässt das Netz eines Besuchers keinen WebSocket durch, wechselt das Widget automatisch auf den HTTP-Rückfall. Er läuft über denselben Pfad /rc_wss/ und braucht keinen weiteren Eintrag im Proxy. Dafür ist zweierlei nötig:

Die Vorlagen

Die folgenden Vorlagen entsprechen denen der Kontoseite. Die Kontoseite setzt Ihren Host, die für Ihr Konto gültigen Zieladressen und – direkt nach dem Erzeugen – Ihren Schlüssel bereits ein. Kopieren Sie die Vorlage deshalb am besten von dort; die Fassungen hier dienen zum Nachlesen. Platzhalter sind chat.ihre-domain.de, SITE_KEY und __RC_PROXY_KEY__.

nginx

Gehört in den http-Kontext, zum Beispiel als /etc/nginx/conf.d/chat.ihre-domain.de.conf. Auf RHEL und CentOS liegt das CA-Bündel unter /etc/pki/tls/certs/ca-bundle.crt statt /etc/ssl/certs/ca-certificates.crt.

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

    ssl_certificate     /etc/letsencrypt/live/chat.ihre-domain.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/chat.ihre-domain.de/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

Die Vorlage enthält nur den VirtualHost für Port 443. Die Umleitung von Port 80 und die Ausstellung des Zertifikats richten Sie ein wie für Ihre übrigen Hosts. SSLProxyVerifyDepth 5 ist nötig: Ohne diese Zeile prüft Apache die Zertifikatskette des Ziels nur eine Stufe tief, und die Verbindung scheitert mit „Certificate Chain too long“.

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

    # 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.ihre-domain.de_access.log combined env=!rc_no_log

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/chat.ihre-domain.de/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/chat.ihre-domain.de/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 holt das Zertifikat selbst, WebSockets brauchen keine eigene Einstellung. header_up mit einem Wert ersetzt, was der Browser unter diesem Namen geschickt hat.

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

Der Proxy-Schlüssel

Fehlerbehebung

HTTP 421 (Misdirected Request)

Unser Server lehnt den Host-Kopf ab: Der Proxy schickt Ihren eigenen Namen statt des Zielnamens. Host-Kopf und SNI müssen der Name des Ziels sein – bei nginx proxy_set_header Host sowie proxy_ssl_server_name on mit proxy_ssl_name, bei Apache ProxyPreserveHost Off, bei Caddy header_up Host und tls_server_name.

HTTP 502 oder SSL-Fehler auf dem Weg zum Ziel

Der Proxy kann die TLS-Verbindung zu uns nicht aufbauen oder traut dem Zertifikat nicht.

HTTP 413 beim Hochladen von Anhängen

Uploads laufen über /rc_wss/. nginx nimmt ohne eigene Einstellung nur 1 MB an; die Vorlage setzt deshalb client_max_body_size 32m im Abschnitt für /rc_wss/. Liegt ein Load-Balancer oder CDN vor Ihrem Proxy, braucht er mindestens dieselbe Grenze.

Chat-Verbindung ohne Upgrade

Die Prüfung meldet „Kein WebSocket-Upgrade“ mit dem HTTP-Status, den sie stattdessen bekommen hat. Der Chat läuft dann über den HTTP-Rückfall weiter, verbindet sich aber erst nach zwei gescheiterten WebSocket-Versuchen.

HTTP 401 oder 403 beim Offline-Formular

DNS zeigt auf eine private Adresse

Die Prüfung meldet „nicht öffentliche Adresse“, wenn mindestens eine Adresse des Namens privat, lokal oder reserviert ist (zum Beispiel 10.0.0.0/8, 192.168.0.0/16, 100.64.0.0/10 oder fc00::/7). Häufige Ursachen sind ein Eintrag aus dem internen DNS oder ein interner Load-Balancer. Ihre Besucher erreichen solche Adressen ebenfalls nicht; der öffentliche Eintrag muss auf eine öffentliche Adresse zeigen.

Von Hand nachsehen

Die Prüfadresse aus Schritt 3 können Sie selbst abrufen:

curl -s https://chat.ihre-domain.de/rc_wss/eigene-domain-probe

Bei richtiger Einrichtung lautet die Antwort:

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

schluessel ist gueltig, unbekannt oder fehlt, client_ip ist gesetzt, fehlt oder ungueltig. site und host stehen nur bei einem gültigen Schlüssel darin. Eine Adresse gibt die Antwort nie zurück.

Stand: