fix(cinny-web): no-cache for the app shell on SPA routes; keep old hashed assets 14 days (#23) #30

Merged
jared merged 1 commits from fix/shell-cache-and-assets into main 2026-10-18 19:44:17 -04:00
Owner

Fixes #23. Production change: merging redeploys the web nginx config and the deploy script, so it waits for an owner's go-ahead.

Problem: SPA URLs (/, /home/…, /login/…) reach index.html through location / { rewrite ^(.+)$ /index.html break; }, which set no cache headers. Browsers then reused the shell heuristically (about 10 % of its age). Meanwhile lotus_deploy.sh ran rsync --delete, which removed the previous build's hashed assets. So after a deploy, a visitor without the service worker could get a stale shell requesting assets/index-<old>.js → 404 → blank app.

Changes

  • cinny/nginx.conf: map $uri $lotus_shell_cache_control adds no-cache, no-store, must-revalidate only when the request was rewritten to /index.html. Other files served by location / are unchanged (the map yields an empty value, so no header is added). location / now includes cinny-security-headers.conf, because an add_header in a location drops the server-level headers (CSP etc.).
  • cinny/lotus_deploy.sh: assets/ is no longer --deleted. New assets are synced, and files older than 14 days are pruned. Unchanged assets get a fresh mtime from the new build, so they're kept.

Verified

  • nginx -t passes in a harness using this repo's snippets (brotli lines stripped, since the module isn't local).
  • Serving a fake webroot through the new config: /, /home/, /login/… and /index.html → Cache-Control: no-cache, no-store, must-revalidate, CSP present. /assets/index-abc.js → public, immutable. /config.json → no-cache. /public/res/sound.ogg → unchanged.
  • bash -n lotus_deploy.sh OK.

After merge: run the usual nginx -t && systemctl reload nginx on LXC 106, then curl -sI https://chat.lotusguild.org/home/ | grep -i cache-control.

🤖 Generated with Claude Code

https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5

Fixes #23. **Production change:** merging redeploys the web nginx config and the deploy script, so it waits for an owner's go-ahead. **Problem:** SPA URLs (`/`, `/home/…`, `/login/…`) reach `index.html` through `location / { rewrite ^(.+)$ /index.html break; }`, which set **no cache headers**. Browsers then reused the shell heuristically (about 10 % of its age). Meanwhile `lotus_deploy.sh` ran `rsync --delete`, which removed the previous build's hashed assets. So after a deploy, a visitor without the service worker could get a stale shell requesting `assets/index-<old>.js` → 404 → blank app. **Changes** - **`cinny/nginx.conf`:** `map $uri $lotus_shell_cache_control` adds `no-cache, no-store, must-revalidate` only when the request was rewritten to `/index.html`. Other files served by `location /` are unchanged (the map yields an empty value, so no header is added). `location /` now includes `cinny-security-headers.conf`, because an `add_header` in a location drops the server-level headers (CSP etc.). - **`cinny/lotus_deploy.sh`:** `assets/` is no longer `--delete`d. New assets are synced, and files older than 14 days are pruned. Unchanged assets get a fresh mtime from the new build, so they're kept. **Verified** - `nginx -t` passes in a harness using this repo's snippets (brotli lines stripped, since the module isn't local). - Serving a fake webroot through the new config: `/`, `/home/`, `/login/…` and `/index.html` → `Cache-Control: no-cache, no-store, must-revalidate`, CSP present. `/assets/index-abc.js` → `public, immutable`. `/config.json` → no-cache. `/public/res/sound.ogg` → unchanged. - `bash -n lotus_deploy.sh` OK. **After merge:** run the usual `nginx -t && systemctl reload nginx` on LXC 106, then `curl -sI https://chat.lotusguild.org/home/ | grep -i cache-control`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared added 1 commit 2026-10-18 01:26:04 -04:00
fix(cinny-web): no-cache for the app shell on SPA routes; keep old hashed assets 14 days (#23)
Lint / Shell (shellcheck) (push) Successful in 24s
Lint / JS (eslint) (push) Successful in 15s
Lint / No secrets in webhook configs (push) Successful in 7s
Lint / Landing page is rendered (matrix (push) Successful in 42s
Lint / Python (ruff) (push) Successful in 11s
Lint / Python deps (pip-audit) (push) Successful in 40s
Lint / Secret scan (gitleaks) (push) Successful in 6s
Lint / Shell (shellcheck) (pull_request) Successful in 9s
Lint / JS (eslint) (pull_request) Successful in 7s
Lint / No secrets in webhook configs (pull_request) Successful in 5s
Lint / Landing page is rendered (matrix (pull_request) Successful in 4s
Lint / Python (ruff) (pull_request) Successful in 6s
Lint / Python deps (pip-audit) (pull_request) Successful in 1m59s
Lint / Secret scan (gitleaks) (pull_request) Successful in 15s
bf770fed83
SPA URLs (/, /home/…, /login/…) are rewritten to /index.html inside
location /, which set no cache headers, so browsers heuristically reused
a stale shell after a deploy, and rsync --delete had just removed the
assets it referenced: a blank app until the shell expired. Send
no-cache for the rewritten shell (map on $uri; location / now includes
the security headers, since add_header drops the inherited ones), and
stop deleting previous builds' hashed assets: prune after 14 days.

Fixes #23

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared merged commit 9b0cbc9db1 into main 2026-10-18 19:44:17 -04:00
Sign in to join this conversation.