https://floe.example.com.
This is the recommended production layout. It means one certificate, one DNS record,
one value to configure, and share links that work without any extra setup.
Route table
The client and the signaling server claim different paths, so they coexist on one origin with no conflicts. Route them like this./api/config is the one exception to the /api/ rule, and it needs its own
route in both configs below. Send it to the signaling server and it returns 404.
With the empty socket URL this page recommends below, the browser still lands on
its own origin, which is the right answer, so the mistake never shows. Any
instance that sets a non-empty socket URL loses it silently.
Proxy configuration
- Caddy
- nginx
Caddy handles HTTPS certificates and WebSocket upgrades automatically, so the
whole config is a handful of lines.
Caddyfile
Floe configuration
Leave the socket URL empty. The browser then talks to whatever host it was loaded from, which behind this proxy is already the right answer..env
Terminal
NEXT_PUBLIC_SITE_URL is the only build-time value here, which is why the
comment above tells you the running stack does not read it. The published image
cannot carry it, so canonical links and share previews are omitted rather than
pointed somewhere wrong. See Container Images to build the
client with your own domain baked in.
CLIENT_URL is matched exactly, including the scheme and with no trailing
slash. https://floe.example.com and https://www.floe.example.com are
different origins to the CORS check, so use whichever one your users actually
visit.TRUSTED_PROXY_COUNT must equal the number of proxy hops in front of the
signaling server. One reverse proxy means 1. A CDN in front of that proxy means
2. Setting it higher than your real hop count lets clients forge
X-Forwarded-For and slip past the per-IP rate limits.
Using the CLI and the desktop app with a one-domain instance
One address is all the CLI needs. The share link it prints resolves to the same origin, so it opens the web client correctly with no extra flag:Terminal
--web flag only exists for split deployments where the client lives on a
different hostname from the signaling server.
The desktop app takes the same single address. Open Settings, expand
Advanced, put https://floe.example.com in Server address, and press
Test. The test runs three checks in order: that /health answers as a Floe
signaling server, that the realtime /ws connection is accepted, and that
GET /api/turn-credentials returns usable connection details. A proxy that
forwards only some of those is named in the failure message instead of breaking
silently later. Leave Share link address blank on a one-domain instance and
the app derives the link from the server address. See
Desktop Settings.
The test deliberately does not follow redirects, so a proxy that answers
/health with a 301 is reported as “may be the web app rather than the
signaling server” rather than passing.TURN does not go through the proxy
If you run the optional coturn relay, its ports stay directly exposed. TURN is not HTTP, so a reverse proxy cannot carry it. See TURN Relay for the port list and firewall rules.Verify it works
Terminal
https://floe.example.com in a browser. The connection badge should read
Ready. If it stays disconnected, the /socket.io/ route is the first thing to
check.
Prefer two subdomains?
Splitting the client and server acrossapp.your-domain.com and
api.your-domain.com is still fully supported. See
Production Deployment.