Appearance
Reverse proxy
Put a reverse proxy in front of the server to handle HTTPS. It has three jobs:
- Pass everything through to the server (port 8800), at the root of its own host name.
- 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. - Send
X-Forwarded-ForandX-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=trueTRUST_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/healthzanswers{"ok":true,...}.- On a page with the chat, open the browser's developer tools, Network tab, filter WS: the socket to
/v1/widget/wsshould show 101 Switching Protocols and stay open. - If that request gets 403 with "Open the chat through its frame",
PUBLIC_URLdoesn't match the address in the browser (a different host, orhttpagainsthttps), orPUBLIC_URLisn't set andTRUST_PROXYis off, so the server thinks it is on plainhttp.