무엇을 도와드릴까요?

문의 내용을 간단히 알려 주세요. 이메일로 답변드릴게요.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
홈으로

자체 도메인과 퍼스트 파티 프록시

웹사이트의 서브도메인으로 위젯 파일, 채팅 연결, 오프라인 양식과 Smart Answer를 제공하는 방법: 프록시 키, 실제 방문자 주소, nginx·Apache·Caddy 템플릿.

자체 도메인을 사용하면 채팅이 chat.your-company.com 같은 웹사이트의 서브도메인을 통해 실행됩니다. 서버는 위젯의 요청을 받아 리버스 프록시로 reactive.chat에 전달합니다. 이 페이지는 해당 서버를 운영하는 사람을 위한 안내입니다. 편집자용 간단한 안내는 자체 도메인(퍼스트 파티)에 있습니다.

서브도메인을 통해 실행되는 항목

얻는 이점:

자체 도메인이 의미하지 않는 사항:

요구 사항

설정

  1. 계정에서 프로젝트의 삽입 → 자체 도메인 설정을 엽니다.
  2. 1. 호스트에 chat.your-company.com 같은 이름을 입력하고 저장합니다.
  3. 2. 프록시 키에서 키 생성을 클릭합니다. 키는 rcpx_로 시작하며 정확히 한 번만 표시됩니다. 해시만 저장합니다. 키는 프록시 설정에만 넣고 웹페이지, 브라우저 스크립트나 저장소에 넣어서는 안 됩니다.
  4. 3. 서버 설정에서 서버에 맞는 템플릿을 가져옵니다. 키 생성 직후에는 키가 포함되어 있으며 나중에는 __RC_PROXY_KEY__가 표시됩니다. 설정을 검사한 뒤(nginx -t 또는 apachectl configtest) 서버 설정을 다시 로드합니다.
  5. 5. 검사에서 지금 검사를 클릭합니다(10초에 최대 한 번). 메시지는 생성되지 않습니다.
  6. 다섯 검사 단계가 모두 “정상”일 때만 삽입 스니펫을 4. 삽입 스니펫의 것으로 교체합니다.
<script src="https://chat.your-company.com/loader.js"
        data-rc-site-key="SITE_KEY"
        data-rc-ws-url="wss://chat.your-company.com/rc_wss/"
        async></script>

위젯은 src에서 파일 위치를, data-rc-ws-url에서 채팅 연결을 가져옵니다. 여기에서 오프라인 양식, 업로드와 HTTP 대체 연결 주소를 도출합니다. 다른 속성은 그대로 유지합니다(script 태그 참조).

프록시가 전달해야 하는 항목

모든 경로에 적용됩니다.

/rc_wss/, /api/offline/, /api/suche/에서는 프록시가 모든 요청에 헤더 두 개를 설정합니다.

“설정”은 브라우저가 이 이름으로 보낸 내용을 덮어쓴다는 뜻입니다. 배포 네트워크로 가는 경로에서는 둘 다 지웁니다.

로드 밸런서나 CDN 뒤에서는 프록시가 처음에 해당 장치의 주소를 봅니다. 실제 주소를 사용하도록 설정합니다. nginx real_ip(set_real_ip_from, real_ip_header), Apache mod_remoteip(RemoteIPHeader, RemoteIPTrustedProxy), Caddy trusted_proxies와 {remote_host} 대신 {client_ip}입니다. 검사는 이 경우를 감지하지 못하며 유효한 주소가 설정되었는지만 확인합니다.

nginx 템플릿

계정 페이지는 호스트, 대상 주소와 키를 미리 채우므로 그곳에서 복사하는 것이 가장 좋습니다. 여기 버전은 참고용이며 자리 표시자는 chat.your-company.com, SITE_KEY, __RC_PROXY_KEY__입니다. RHEL/CentOS의 CA 번들은 /etc/pki/tls/certs/ca-bundle.crt에 있습니다.

server {
    listen 80;
    listen [::]:80;
    server_name chat.your-company.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-company.com;

    ssl_certificate     /etc/letsencrypt/live/chat.your-company.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/chat.your-company.com/privkey.pem;

    # HTTP fallback (SSE / long polling): token in the query, do not 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;
    }

    # Chat connection (WebSocket) and uploads
    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;
        proxy_buffering off;
        proxy_cache off;
        client_max_body_size 32m;
        proxy_request_buffering off;
    }

    # Offline form
    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 (website search), answer as a stream
    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;
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 120s;
    }

    # Widget files
    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;
    }
}

Apache 템플릿(2.4.47 이상)

443 포트의 VirtualHost만 있습니다. 80 포트에서의 리디렉션과 인증서는 다른 호스트처럼 설정합니다. SSLProxyVerifyDepth 5가 필요합니다. 없으면 Apache가 대상 체인을 한 단계만 검사하고 “Certificate Chain too long”을 보고합니다.

<VirtualHost *:443>
    ServerName chat.your-company.com

    SetEnvIf Request_URI "^/rc_wss/h/" rc_no_log
    CustomLog ${APACHE_LOG_DIR}/chat.your-company.com_access.log combined env=!rc_no_log

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/chat.your-company.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/chat.your-company.com/privkey.pem

    SSLProxyEngine on
    SSLProxyVerify require
    SSLProxyVerifyDepth 5
    SSLProxyCheckPeerName on
    SSLProxyCACertificateFile /etc/ssl/certs/ca-certificates.crt
    ProxyRequests Off
    ProxyPreserveHost Off
    ProxyAddHeaders Off

    RequestHeader unset X-RC-Proxy-Key early
    RequestHeader unset X-RC-Client-IP early
    RequestHeader unset Cookie early
    RequestHeader unset Authorization early

    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 이전 버전은 upgrade=websocket을 지원하지 않습니다. 이 경우 첫 번째 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 2 템플릿

값이 있는 header_up은 브라우저가 이 이름으로 보낸 내용을 대체합니다. 접근 로그를 켜면 /rc_wss/h/*를 제외합니다(log_skip, Caddy 2.8 이전에는 skip_log).

chat.your-company.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
            }
            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
            }
        }
    }
}

다섯 검사 단계

각 단계는 “정상”, “오류”, “안내”, “검사하지 않음” 중 하나입니다. 다섯 번 모두 “정상”이어야 통과입니다. 검사는 HTTPS 443 포트만 사용하고 리디렉션을 따르지 않으며 단계당 5초입니다.

  1. 이름 확인 – A/AAAA가 있고 모든 주소가 공인 주소입니다. 이후 단계는 정확히 이 주소에 연결합니다.
  2. 자체 도메인의 loader.js – 파일을 로드해 당사 파일과 비교합니다. 연결, 응답이나 유효한 인증서가 없으면 나머지 단계를 건너뜁니다. 리디렉션은 오류이며 다른 파일은 캐시나 /의 잘못된 대상을 나타냅니다.
  3. 키와 방문자 주소 – /rc_wss/eigene-domain-probe를 호출합니다. 가능한 오류는 X-RC-Proxy-Key 누락, 알 수 없는 키(생성 직후 최대 30초 지연), X-RC-Client-IP 누락 또는 유효하지 않음, 다른 프로젝트의 키입니다. 키가 유효하지만 다른 호스트에 등록되어 있으면 안내입니다.
  4. WebSocket – wss://chat.your-company.com/rc_wss/는 HTTP 101과 rc.v1 하위 프로토콜로 응답해야 합니다.
  5. 오프라인 양식 – GET /api/offline/SITE_KEY는 405로 응답해야 합니다(POST만 허용하는 엔드포인트이므로 메시지를 만들지 않고 검사 가능).

3단계의 검사 주소를 직접 호출할 수도 있습니다.

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

올바르게 설정된 경우:

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

프록시 키

문제 해결

다음: 전송과 프로토콜 · 보안과 CSP

최종 업데이트: