Skip to content

Reverse proxy ​

Put a reverse proxy in front of the server to handle HTTPS. It has three jobs:

  1. Pass everything through to the server (port 8800), at the root of its own host name.
  2. Pass WebSocket upgrades for /v1/widget/ws (the chat) and /api/v1/live (the admin panel's live updates). Without them the chat shows "Reconnecting..." forever.
  3. Send X-Forwarded-For and X-Forwarded-Proto, and allow uploads of at least 26 MB.

Then set, in the server's environment:

sh
PUBLIC_URL=https://chat.example.com
TRUST_PROXY=true

TRUST_PROXY makes the server believe the proxy's X-Forwarded-* headers: rate limits see each visitor's own IP, and the server knows requests came over https. Only set it when the server can't be reached except through the proxy, or anyone could fake those headers.

Caddy ​

Caddy gets and renews certificates by itself, and passes WebSockets and X-Forwarded-* headers by default:

text
chat.example.com {
	reverse_proxy 127.0.0.1:8800
}

nginx ​

nginx
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 443 ssl;
    http2 on;
    server_name chat.example.com;

    ssl_certificate     /etc/letsencrypt/live/chat.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;

    # image uploads (up to 25 MB) and knowledge files
    client_max_body_size 30m;

    location / {
        proxy_pass http://127.0.0.1:8800;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # WebSockets: the chat (/v1/widget/ws) and the admin panel (/api/v1/live)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        # voice conversations are long-lived sockets
        proxy_read_timeout 1h;
        proxy_send_timeout 1h;
    }
}

server {
    listen 80;
    server_name chat.example.com;
    return 301 https://$host$request_uri;
}

The server pings each chat socket every 20 seconds, so nginx's default 60-second read timeout would also do; the longer one leaves room for slow networks.

Other proxies and load balancers ​

  • Cloudflare: WebSockets are on by default. Use "Full (strict)" TLS to the origin.
  • AWS ALB, Google Cloud Load Balancing, Traefik, HAProxy: they pass WebSockets; raise idle timeouts to several minutes for voice.
  • Run one server process behind it (see Docker).

Checking it ​

  • https://chat.example.com/healthz answers {"ok":true,...}.
  • On a page with the chat, open the browser's developer tools, Network tab, filter WS: the socket to /v1/widget/ws should show 101 Switching Protocols and stay open.
  • If that request gets 403 with "Open the chat through its frame", PUBLIC_URL doesn't match the address in the browser (a different host, or http against https), or PUBLIC_URL isn't set and TRUST_PROXY is off, so the server thinks it is on plain http.

Wireface Chat 0.1.0. These docs are served by your own server.