diff --git a/docs/gotchas.md b/docs/gotchas.md index b7b2442e..abda3540 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -140,6 +140,35 @@ store files. Any subsequent write into the copied tree (adding new files in subdirectories) fails with `EPERM`. Fix: pass `--no-preserve=mode,ownership` so the output tree is writable. +## SPA fallback: use `Accept` header map, not `try_files ... /index.html` + +The naive nginx pattern for a path-prefix SPA (`try_files $uri $uri/ +/matrix/index.html`) silently swallows asset 404s — a missing JS file +returns `index.html` with a 200, so the JS runtime never loads and the +page renders blank with no visible error (#685, fixed #684, narrowed #686). +Extension allowlists (tried in #686 → PR #729) have the same maintenance +problem: any new file extension the SPA ships breaks silently. + +The correct pattern (landed in PR #729, `hive-gateway.nix`) keys the +fallback on the HTTP `Accept` header: + +```nginx +# Outside the server block (appendHttpConfig): +map $http_accept $matrix_spa_target { + default "/__matrix_spa_no_html_fallback"; + "~*text/html" "/matrix/index.html"; +} + +# Inside the location: +try_files $uri $uri/ $matrix_spa_target =404; +``` + +Top-frame navigations always send `Accept: text/html,...` (chrome / +firefox / safari are consistent). Asset fetches (`image/*`, +`application/javascript`, `*/*`) don't carry `text/html`, so they +fall through to the trailing `=404`. No extension list to maintain; +no named-location indirection needed. + ## `hive-forge`: prefer over raw curl pipelines Every agent container has `hive-forge` in PATH (installed via