The simplest production layout puts the client and the signaling server behind
one domain, so there is a single certificate and a single URL to configure.
See Reverse Proxy for copy-pasteable Caddy and
nginx configuration. This page covers the two-subdomain layout, which is still
fully supported.
Steps
1
Put both services behind a reverse proxy with HTTPS
Browsers require a secure context (HTTPS) for WebRTC and the Screen Wake Lock API. Use Nginx, Caddy, Traefik, or a managed platform like Render or Fly.Proxy each service to a hostname you control:
app.your-domain.comto the client on:3000api.your-domain.comto the signaling server on:3001
Upgrade and Connection headers.2
Set NEXT_PUBLIC_SOCKET_URL and rebuild
In Then restart. No rebuild: the client reads this address at runtime.
.env:3
Set CLIENT_URL
In This adds your client origin to the server’s CORS allow-list. Without it, browser requests from your domain will be blocked.
.env:4
Set TRUSTED_PROXY_COUNT
Set this to the number of proxy hops in front of the signaling server so per-IP rate limiting reads real client IPs instead of proxy IPs:One reverse proxy is
1, a CDN in front of that proxy is 2. The server’s own default is 1; Docker Compose passes 0 explicitly, which is correct only when the container ports are published directly. Setting it higher than your real hop count lets clients forge X-Forwarded-For and bypass the per-IP rate limits, so if you expose the server directly with no proxy, set 0 deliberately rather than leaving it unset.CORS note
The server’s allow-list also includes the officialfloe.one origins (https://floe.one and https://www.floe.one) plus http://localhost:3000 for local development. Your CLIENT_URL is added on top of these, so browser clients on your domain and on floe.one can both reach your server. If you are running a private fork and want a strict allow-list, remove the hard-coded entries from server/server.js.