---
title: "Terminate TLS"
summary: "The two supported TLS patterns, and the proxy-trust setting that has to accompany the second one."
description: "Serve Fanout over TLS directly, or behind a reverse proxy, for both application traffic and ingest."
status: preview
read_when:
  - "You are exposing an instance beyond a trusted network."
  - "Browser session cookies are not coming back Secure."
---
Fanout serves the browser, API, MCP and both OTLP transports on one listener.
Use direct TLS or terminate TLS at a reverse proxy.

## Direct TLS

Set both files; supplying only one fails startup:

```sh
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.

## Behind a reverse proxy

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

```sh
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 to
mark browser session cookies `Secure` when TLS terminates at the proxy.

Without local TLS or an HTTPS `FANOUT_PUBLIC_URL`, browser session cookies lack
`Secure`, and Fanout warns 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 attribution and sign-in rate limits use the client address.
Trusting every source lets any caller claim any address. Fanout warns
when a public URL is set, TLS is not terminated locally, and this is empty.

### HTTP/2 to the backend

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](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy)
can serve all surfaces with:

```text
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](https://docs.cloud.google.com/run/docs/configuring/http2); 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.

## External MCP clients

For `FANOUT_PUBLIC_URL` requirements, see [MCP setup](/docs/guides/mcp#set-the-public-url-first).

MCP also validates `Origin` before authentication; see
[Connect an agent over MCP](/docs/guides/mcp#protocol).