v0.49.4 — Public URL override for reverse-proxy installs
Added
"Public URL" setting under Settings → App. Operator-supplied override for the URL Tesserae uses when building external links (OAuth callbacks, HA discovery image URLs, etc.). Use this when running behind a reverse proxy whose `X-Forwarded-*` headers don't reach Flask cleanly.
Background
0.49.3 added ProxyFix to honour `X-Forwarded-Proto` / `X-Forwarded-Host` / `X-Forwarded-Port` from upstream reverse proxies. That works for proxies that send those headers as standard, but NGINX Proxy Manager has an undocumented quirk: it silently ignores `proxy_set_header` directives in its Advanced tab unless they're inside a Custom Location block. NPM's own warning text says "any add_header or set_header directives added here will not be used by nginx," but it's easy to miss, and the Custom Location workaround is non-obvious.
Setting Public URL bypasses that mess entirely. One value, applied at the WSGI layer, force-overrides scheme + host + port on every request.
Usage
In Tesserae:
- Settings → App
- Find the new Public URL field at the top
- Paste the full public URL: scheme + host + port if non-standard
- Save
Example values:
- `https://tesserae.example.org\` (standard 443)
- `https://tesserae.example.org:8443\` (non-standard port, e.g. when ISP blocks 443 inbound)
- `https://homeassistant.tail1234.ts.net\` (Tailscale Funnel)
Leave blank for the existing auto-detect behaviour (ProxyFix + request headers). Malformed values silently fall back to auto-detect so a typo doesn't lock you out.
Implementation
Thin WSGI middleware reads the setting per request (no Tesserae restart needed when changing it), parses the URL, and rewrites `wsgi.url_scheme` + `HTTP_HOST` on the environ. Trailing slashes are stripped tolerantly.
Tests
+4 regression tests in `tests/test_proxyfix.py` covering override, trailing-slash tolerance, empty-falls-back-to-ProxyFix, and graceful fallback when the value is malformed. 979 passing, ruff + mypy strict clean.