fix(files): general.host must be a bare hostname, not a URL

The SPA computes its redirect origin as (force_ssl ? "https://" : "http://")
+ general.host. With host set to "https://files.${BASE_DOMAIN}" that produced
"http://https://files.ulicraft.net", so every client-side redirect broke and
the page never routed anywhere.

Server-side this was invisible: SecureOrigin strips the scheme before the
Host check, so `curl /` returned 200, the API answered, and the uptime
monitor stayed green — only real browsers failed. The tell is a
`POST /report?...msg=Redirecting to http://https://...` line in the log.

Set host to the bare hostname and force_ssl=true (which the origin needs to
build https://, and which only emits an HSTS header — it does not redirect,
so it can't loop behind the plain-http nginx→caddy hop).

Verified: origin is now "https://files.ulicraft.net"; login + ls still work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-14 18:27:42 +02:00
parent cd79b0e540
commit 10b86ae23e
3 changed files with 38 additions and 7 deletions

View File

@@ -121,12 +121,35 @@ config nobody can log into. (The *admin* password is checked by Go's bcrypt
directly, which does accept `$2y$` — but `caddy hash-password` gives `$2a$`
anyway, so just use that for both.)
### `general.host` must match the incoming `Host` header
Filestash blocks every non-`/admin/` request whose `Host` differs from its
configured `general.host`, with *"only traffic from X is allowed"*. It's a 403
that looks like an auth failure. nginx forwards the original Host and caddy
passes it through — don't rewrite it anywhere. If `files.` starts 403ing after a
domain change, this is why.
### `general.host` is a BARE HOSTNAME — a scheme breaks the browser only
`"host": "files.ulicraft.net"`, **not** `"https://files.ulicraft.net"`, plus
`"force_ssl": true`. The SPA computes its redirect origin as
`(force_ssl ? "https://" : "http://") + host`, so a scheme in `host` yields
**`http://https://files.ulicraft.net`** and every client-side redirect dies —
the page loads, then silently fails to route anywhere.
The vicious part: the server-side Host check (`SecureOrigin`) **strips** the
scheme before comparing, so the server is perfectly happy. `curl /` returns 200,
the API answers, and the Uptime Kuma monitor stays green — **only real browsers
break**. Symptom in the log is a `POST /report?...msg=Redirecting to
http://https://...` line. Hit this on 2026-07-14; fixed in `config.json.tmpl`.
`force_ssl` only sets an HSTS header — it does **not** redirect, so it can't loop
behind the nginx→caddy (plain http) hop. It must be `true` or the SPA builds
`http://` origins on an https page.
### `general.host` must also match the incoming `Host` header
Filestash blocks every non-`/admin/` request whose `Host` differs from
`general.host`, with *"only traffic from X is allowed"* — a 403 that looks like
an auth failure. nginx forwards the original Host and caddy passes it through —
don't rewrite it anywhere.
### The login form only renders at `?action=redirect`
`GET /api/session/auth/` renders the htpasswd form **only** when
`action=redirect` is present; any other request falls through to the credential
check, fails with empty creds, and 303s back to `?action=redirect`. So the real
form URL is `/api/session/auth/?action=redirect&label=files`. A bare
`/api/session/auth/?label=files` returning a 303 is **normal**, not a bug.
### `config.json` is a single-file bind mount → stale inode
Same shape as the caddy `conf.d` gotcha. The mount pins the **inode**, so