Documentation
API reference

Start here

Search every guideesc close

Connections

Use your own tunnel or domain

Reach your server at full quality through Tailscale, a Cloudflare Tunnel, or your own reverse proxy, with no port forwarding.

6 min read

If your provider won't let you open a port, or you simply prefer not to, you can point tofa at an address you already control. Apps then connect through it at full quality, with none of the relay's bandwidth caps.

This is one line of configuration on the tofa side. Add the address under Admin, Settings, Remote Access, Custom access URLs, and we advertise it to your apps as another direct path. Everything else on this page is about getting that address to exist in the first place.

This adds a path, it does not replace one

Your apps race every path they know about and use whichever answers first. A tunnel sits alongside local playback, port forwarding, and the relay. If the tunnel is down, playback still works.

What the address has to look like#

We validate the URL when you add it, so it is worth knowing the rules up front. The address must:

  • use https://, with a certificate a normal browser trusts. Plain HTTP is rejected, because secure clients cannot reach it.
  • point at the server's root. https://tofa.example.com is fine, https://example.com/tofa is not. tofa does not serve under a sub-path.
  • carry no query string, fragment, or embedded username and password.
  • not sit behind a second login. Cloudflare Access, an authentication proxy, or HTTP basic auth in front of the server will block both our reachability check and every app. tofa does its own authentication; that is what the address is for.
  • pass WebSocket connections through. Apps hold a live connection for playback state and library updates.

A port is allowed if you need one, for example https://tofa.example.com:8443. You can add up to three addresses, which is useful if you run both a tunnel and your own domain.

Tailscale#

The quickest option if the only people watching are you and your own devices. Tailscale gives your server a name and a real certificate on your private network, and nothing is exposed to the public internet.

Usually there is nothing to configure

If Tailscale is installed on the machine running tofa, and tofa can see it (running directly on the machine, or in a container on the host network), we find the tailnet address on our own and advertise it for you. Your apps pick it up on the next check-in, and Remote Access shows it as a private network path. You do not need the tailscale serve step or a custom access URL.

The one case that still needs the manual steps below is a container on a bridge network, which cannot see the host's tailnet address at all.

  1. Install Tailscale on the machine running tofa, and on every device you want to watch on. There are apps for iPhone, iPad, Apple TV, Android, and Android TV. There is no Tailscale app for webOS or Tizen televisions, so the built-in TV apps cannot use this path.

  2. In the Tailscale admin console, turn on MagicDNS and HTTPS Certificates.

  3. On the server, publish tofa on your tailnet:

    tailscale serve --bg --https=443 http://localhost:33333

    Run tailscale serve status to see the resulting address. It looks like https://your-machine.your-tailnet.ts.net.

  4. Add that address under Custom access URLs.

Only tailnet devices can use it

People you share your server with are not on your tailnet, so they keep using the relay unless you invite them to it. If you want a path that works for everyone, use a tunnel or your own domain instead.

Tailscale Funnel makes a tailnet service public, but it routes your traffic through Tailscale's own infrastructure and is not built for sustained video. We do not recommend it for streaming.

Cloudflare Tunnel#

A public hostname that works for everyone you share with, and needs no open port. Read the note at the end of this section before committing to it.

  1. Install cloudflared on the machine running tofa and sign in:

    cloudflared tunnel login
    cloudflared tunnel create tofa
  2. Point a hostname at the server's root. In your cloudflared config:

    tunnel: <your-tunnel-id>
    credentials-file: /root/.cloudflared/<your-tunnel-id>.json
    ingress:
      - hostname: tofa.example.com
        service: http://localhost:33333   # no path, the server's root
      - service: http_status:404
  3. Create the DNS record and run the tunnel as a service:

    cloudflared tunnel route dns tofa tofa.example.com
    cloudflared service install
  4. Leave WebSocket support on, and do not put a Cloudflare Access policy in front of the hostname.

  5. Add https://tofa.example.com under Custom access URLs.

Cloudflare's terms limit video through their network

Cloudflare restricts serving large amounts of video and other non-web content through their proxy on their free and lower-tier plans. Streaming a library through a tunnel is exactly that, and people have had tunnels throttled or shut off for it. It works well when it works, but it is worth knowing the risk before you rely on it. A plain reverse proxy on a VPS, or a port forward, carries no such condition.

Your own reverse proxy#

If you already run Caddy, nginx, Traefik, or a bundle like SWAG, point a hostname at the server and add it. With Caddy the whole configuration is:

tofa.example.com {
    reverse_proxy localhost:33333
}

Caddy handles the certificate, WebSocket upgrades, and streaming responses on its own. With nginx, three settings matter and are easy to miss:

location / {
    proxy_pass http://127.0.0.1:33333;

    # WebSockets: live playback state and library updates
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;

    # Video must stream through, not be collected first
    proxy_buffering off;
    proxy_read_timeout 600s;
}

Buffering is the one that bites: with it on, nginx tries to collect each video segment before passing it along, and playback stalls or starts slowly.

Check it worked#

Open Admin, Settings, Remote Access. Each address you add is listed with a badge. We probe it from the cloud on the server's next check-in, so a new address turns Verified within about a minute.

A Tailscale address never gets a verdict, and that is fine. We probe from outside your network, and a .ts.net name or any other private address is only reachable from inside it, so there is nothing for us to reach. Those entries show Private network instead, or Not verified yet on older versions. Neither means anything is wrong: devices on your tailnet still connect through the address at full quality. The rest of this section is about public addresses, such as a tunnel or your own domain.

If a public address stays Not verified yet, work through the list at the top of this page. The usual causes are a login sitting in front of the server, a certificate the probe doesn't trust, or a tunnel pointed at a sub-path rather than the root.

Once it is verified, or once your device is on the tailnet, play something away from home and open the quality menu. The relay notice should be gone, and the full quality ladder available. That is the proof it is being used, rather than just being reachable.

Prefer to keep it in your compose file?

The TOFA_CUSTOM_ACCESS_URLS variable takes the same addresses, separated by commas. Anything set in Settings wins over the variable.