どのようにお手伝いできますか?

ご用件を簡単にお知らせください。メールで返信します。

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
トップページへ

独自ドメイン(ファーストパーティー)

Webサイトのサブドメインからウィジェット、チャット接続、オフラインフォームを配信します。要件、設定、nginx・Apache・Caddy用テンプレート、問題の解決方法を説明します。

独自ドメインでは、chat.your-domain.comなど、Webサイトのサブドメインを通してチャットを運用します。運営側のサーバーがウィジェットのリクエストを受け、リバースプロキシとしてreactive.chatに転送します。このページは、そのサーバーの管理者向けです。

独自ドメインでできること

次の通信がサブドメインを経由します。

その結果、次のようになります。

独自ドメインで変わらないこと

要件

ロードバランサーやCDNの背後では、プロキシに最初に見えるのは訪問者ではなく、その機器のアドレスです。その場合は、実際の訪問者のアドレスを引き継ぐように設定します。nginxではreal_ipモジュール(set_real_ip_from、real_ip_header)、Apacheではmod_remoteip(RemoteIPHeader、RemoteIPTrustedProxy)、Caddyではtrusted_proxiesオプションと、{remote_host}の代わりに{client_ip}プレースホルダーを使います。当社の確認では、この状況を検出できません。有効なアドレスが設定されているかは分かりますが、どのアドレスかまでは分かりません。

設定手順

  1. ページを開きます。 アカウントでプロジェクトを開きます。「埋め込み」セクションに独自ドメインを設定リンクがあります。
  2. ホストを入力します。 「1. ホスト」にchat.your-domain.comなどの名前を入力し、保存します。
  3. キーを作成します。 「2. プロキシキー」でキーを作成をクリックします。キーはrcpx_で始まり、表示されるのは1回だけです。当社はハッシュだけを保存します。すぐにコピーしてください。秘密情報であり、プロキシの設定だけに置きます。Webページ、ブラウザーのスクリプト、リポジトリには絶対に含めないでください。
  4. テンプレートを挿入します。 「3. サーバー設定」に、nginx、Apache、Caddy用のテンプレートがあります。キーの作成直後には実際のキーが入り、その後は__RC_PROXY_KEY__が表示されるので、キーに置き換えます。必要なら証明書のパスを調整し、設定をテストして(nginx -tまたはapachectl configtest)、サーバーを再読み込みします。
  5. 確認します。 「5. 確認」で今すぐ確認をクリックします。当社が外部からサブドメインを呼び出し、5つの経路を個別に確認します(下記参照)。メッセージは作成しません。再確認は、早くても10秒後に可能です。
  6. スニペットを置き換えます。 5つのステップがすべて「OK」になってから、Webサイトの既存の埋め込みスニペットを、「4. 埋め込みスニペット」のものに置き換えます。

新しいスニペットは次のようになり、両方のアドレスがサブドメインを指します。

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

ウィジェットはsrcからファイルの場所を、data-rc-ws-urlからチャット接続先を取得し、そこからオフラインフォームのアドレスを導きます。data-rc-localeなどの他の属性はそのままで、ウィジェットの埋め込みで説明しています。アカウントページのスニペットには、ブランドの色も含まれています。

5つの確認ステップ

各ステップには「OK」「エラー」「注意」「未確認」が表示されます。5つすべてが「OK」の場合だけ合格です。注意は合格にはなりません。確認はポート443のHTTPSだけを使用し、リダイレクトを追跡せず、各ステップの制限時間は5秒です。

  1. 名前解決。 DNSレコードが存在し、すべてのアドレスが公開アドレスである必要があります。AもAAAAも見つからない場合や、1つでもプライベート、ローカル、予約済みのアドレスがある場合は報告します。そのようなアドレスは確認しません。以降のすべてのステップは、ここで確認したアドレスに正確に接続します。
  2. 独自ドメイン経由のloader.js。 https://chat.your-domain.com/loader.js を読み込み、当社のファイルと比較します。接続、応答、有効な証明書のいずれかが得られない場合は、残りをスキップします(「未確認」)。プロキシがファイルではなくリダイレクトを返した場合はエラーです。当社のファイルと異なる場合は、プロキシのキャッシュや/の転送先の誤りが一般的な原因です。
  3. キーと訪問者のアドレス。 /rc_wss/配下の確認用アドレスを呼び出します。当社のサーバーは、有効なキーと訪問者のアドレスを受信したか、キーがどのプロジェクトに属するかを返します。考えられるエラーは、X-RC-Proxy-Keyヘッダーがない、キーが不明(入力ミス、古い、削除済み。作成直後は最大30秒かかる場合があります)、X-RC-Client-IPヘッダーがない、または有効なアドレスを含まない、キーが別のプロジェクトに属する、というものです。キーは有効でも別のホストに登録されている場合は、注意が表示されます。
  4. チャット接続(WebSocket)。 wss://chat.your-domain.com/rc_wss/経由でWebSocket接続を開き、rc.v1プロトコルでHTTP 101が返ることを期待します。「WebSocketへのアップグレードなし」は、プロキシがUpgradeヘッダーとConnectionヘッダーを転送していないことを意味します。
  5. オフラインフォーム。 https://chat.your-domain.com/api/offline/SITE_KEY をGETで呼び出します。エンドポイントはPOSTだけを受け付けるため、HTTP 405で応答します。これにより、メッセージを作成せずに到達を確認できます。他の結果には、オリジンの拒否(注意。トラブルシューティング参照)、プロジェクトが無効、転送先が認証を要求(HTTP 401)があります。

ステップ2~5では、HTTP 421も返ることがあります(Hostヘッダー。トラブルシューティング参照)。ステップ3または5で「reactive.chatからの応答ではありません」と表示された場合は、プロキシのそのパスが誤った転送先を指しています。

プロキシが転送する必要のある内容

3つの経路すべてに、次を適用します。

/rc_wss/と/api/offline/では、プロキシが各リクエストに2つのヘッダーを設定します。

「設定する」とは、ブラウザーがこれらの名前で送った値を上書きし、当社には一切届かないようにすることです。配信ネットワークへの経路では、両方のヘッダーを削除します。キーを送るのはチャットサーバーだけです。

独自ドメイン経由のHTTPフォールバック

訪問者のネットワークがWebSocketを通さない場合、ウィジェットは自動的にHTTPフォールバックに切り替えます。同じ/rc_wss/パスを使うため、プロキシに追加の項目は不要です。次の2点が必要です。

テンプレート

以下のテンプレートは、アカウントページのものと対応しています。アカウントページでは、ホスト、アカウントで有効な転送先アドレス、キーの作成直後であればキーも入力済みです。そのため、そちらからコピーすることをお勧めします。 ここに掲載するものは参照用です。プレースホルダーはchat.your-domain.com、SITE_KEY、__RC_PROXY_KEY__です。

nginx

httpコンテキストに配置します。たとえば/etc/nginx/conf.d/chat.your-domain.com.confです。RHELとCentOSのCAバンドルは、/etc/ssl/certs/ca-certificates.crtではなく/etc/pki/tls/certs/ca-bundle.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

テンプレートに含まれるのは、ポート443のVirtualHostだけです。ポート80からのリダイレクトと証明書は、他のホストと同じように設定してください。SSLProxyVerifyDepth 5が必要です。指定しないと、Apacheは転送先の証明書チェーンを1段階しか検証せず、「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は証明書を自動で取得し、WebSocketに追加設定は不要です。値付きのheader_upは、ブラウザーが同じ名前で送った内容を置き換えます。

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

プロキシキー

トラブルシューティング

HTTP 421(Misdirected Request)

当社のサーバーがHostヘッダーを拒否しています。プロキシが転送先ではなく、自身の名前を送っています。HostヘッダーとSNIには、転送先の名前が必要です。nginxではproxy_set_header Hostと、proxy_ssl_nameを指定したproxy_ssl_server_name on、ApacheではProxyPreserveHost Off、Caddyではheader_up Hostとtls_server_nameを使います。

転送先とのHTTP 502またはSSLエラー

プロキシが当社とのTLS接続を確立できないか、証明書を信頼していません。

添付ファイルのアップロードでHTTP 413

アップロードは/rc_wss/を経由します。個別に設定しないとnginxは1 MBしか受け付けないため、テンプレートでは/rc_wss/セクションにclient_max_body_size 32mを設定しています。プロキシの前にロードバランサーやCDNがある場合は、少なくとも同じ上限が必要です。

アップグレードされないチャット接続

確認で「WebSocketへのアップグレードなし」と、代わりに受信したHTTPステータスが表示されます。この場合、チャットはHTTPフォールバックで続行しますが、WebSocketが2回失敗してから接続します。

オフラインフォームでHTTP 401または403

DNSがプライベートアドレスを指す

その名前のアドレスに1つでもプライベート、ローカル、予約済みのアドレス(10.0.0.0/8、192.168.0.0/16、100.64.0.0/10、fc00::/7など)があると、非公開アドレスとして報告します。内部DNSのレコードや内部ロードバランサーが一般的な原因です。訪問者もそのようなアドレスには到達できません。公開レコードは公開アドレスを指す必要があります。

手動で確認する

ステップ3の確認用アドレスを自分で呼び出すこともできます。

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

正しく設定されていれば、次の応答が返ります。

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

値はドイツ語です。schluessel(キー)はgueltig(有効)、unbekannt(不明)、fehlt(なし)のいずれかです。client_ipはgesetzt(設定済み)、fehlt(なし)、ungueltig(無効)のいずれかです。siteとhostは、キーが有効な場合だけ含まれます。応答にアドレスが含まれることはありません。

ドキュメントの関連項目

詳しくは、独自ドメインとファーストパーティープロキシをご覧ください。すべての章はドキュメントにあります。

最終更新: