Skip to content

Reverse Proxy and HTTPS

Iain Smith edited this page Sep 2, 2026 · 2 revisions

Reverse Proxy and HTTPS

Passkeys and Secure cookies need HTTPS — or an origin of localhost. On a bare LAN IP you can still use the password + TOTP fallback, but for passkeys put tvtimes behind a TLS-terminating proxy.

Set the origin

In .env:

TVTIMES_PUBLIC_ORIGIN=https://tv.example.com
TVTIMES_WEBAUTHN_RP_ID=tv.example.com

Then recreate the containers. TVTIMES_WEBAUTHN_RP_ID is the registrable domain only — no scheme, no port. Changing it later invalidates every existing passkey.

Proxy rules

  • Terminate TLS at the proxy and forward all paths to the app container — the SPA and API are one origin, so there's nothing to split.
  • Forward X-Forwarded-Proto and X-Forwarded-For, then set TVTIMES_TRUSTED_PROXIES to your proxy's address(es) — a comma-separated list of IPs / CIDRs. The app reads the client IP itself (rate limiter, audit log) and trusts X-Forwarded-For only from those addresses; left empty, every request is treated as direct so a client can't spoof its IP. A same-host proxy is 127.0.0.1; the compose network is 172.16.0.0/12.

Caddy

tv.example.com {
    reverse_proxy 127.0.0.1:8888
}

Nginx

location / {
    proxy_pass http://127.0.0.1:8888;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Response headers

The app sends its own security headers — CSP, X-Content-Type-Options, Referrer-Policy, X-Frame-Options, and HSTS when TVTIMES_PUBLIC_ORIGIN is https:// in env=prod. You don't need to add them at the proxy (and shouldn't duplicate the CSP).

The CSP allows img-src … https://image.tmdb.org; channel logos are proxied through this origin (/api/channels/{id}/logo), so the only cross-origin asset is TMDB artwork and the iptv-org logo CDN is fetched server-side only. The refresh cookie is HttpOnly; Secure; SameSite=Lax, scoped to /api/auth; the CSRF cookie is non-HttpOnly at path /.

Clone this wiki locally