Skip to content

Terminate TLS

Fanout serves the browser, API, MCP and both OTLP transports on one listener. Use direct TLS or terminate TLS at a reverse proxy.

Set both files. Setting one without the other is a startup error rather than a half-configured listener:

Terminal window
export FANOUT_TLS_CERT_FILE=/etc/fanout/tls/cert.pem
export FANOUT_TLS_KEY_FILE=/etc/fanout/tls/key.pem

That enables TLS 1.3 on the shared listener. Browser traffic and OTLP/HTTP use HTTPS; gRPC uses HTTP/2 negotiated through ALPN.

Terminate at the proxy and forward to Fanout’s shared port (7520 by default). Two settings have to come with it:

Terminal window
export FANOUT_PUBLIC_URL=https://fanout.example.com
export FANOUT_TRUSTED_PROXY_CIDRS=10.0.1.0/24

FANOUT_PUBLIC_URL is the externally reachable HTTPS origin. Fanout uses it as a trusted signal that TLS is terminated somewhere in front, which is what lets it mark session cookies Secure. Without it, an instance behind a proxy issues cookies that are not — and it warns about exactly that at startup.

FANOUT_TRUSTED_PROXY_CIDRS is the list of networks whose forwarded client addresses Fanout will believe. Set it to the proxy network and nothing wider. Never 0.0.0.0/0: audit records and rate limits both key on client address, and trusting every source means any caller can claim to be any address. Fanout warns when a public URL is set, TLS is not terminated locally, and this is empty.

For gRPC, the proxy must forward HTTP/2 to Fanout. If Fanout has no local certificate, use an h2c upstream (plaintext HTTP/2 with prior knowledge). Forwarding HTTP/1.1 supports the UI, API and OTLP/HTTP, but breaks gRPC. Preserve the original Host header for MCP, and allow long-lived SSE responses.

For example, Caddy’s h2c upstream can serve all surfaces with:

fanout.example.com {
reverse_proxy h2c://127.0.0.1:7520
}

On a platform that supplies one port, set FANOUT_ADDR to that port (for example :8080). For Cloud Run, enable end-to-end HTTP/2; Fanout accepts its h2c backend connections. A dedicated ingest hostname can still point at the same backend port. Enter that hostname on Connect telemetry before copying the Collector configuration; it belongs to the collector’s network route, not the server listener configuration.

FANOUT_PUBLIC_URL above does double duty: Fanout derives the MCP resource URI from it, and that derived resource is what tokens are bound to as their audience. So it must be HTTPS and stable across restarts — changing it invalidates every token already issued. FANOUT_PUBLIC_URL is the only public-origin setting.