The page's index.html is served without Cache-Control, so a cached copy can point at assets a new deploy no longer has #141

Closed
opened 2026-10-14 16:08:46 +02:00 by clawbot · 2 comments
Collaborator

configs/nginx.conf lines 33 to 35 (location /), at v1.0.0-rc1: the page (index.html) is served with Last-Modified and ETag and no Cache-Control. Browsers then cache it heuristically (a share of its age since Last-Modified, which is the build time). The page points at hashed files under /assets/, which are served immutable and get new names on every build. After a deploy, a browser that still holds the old index.html asks for assets the new image no longer has, nginx answers 404, and the visitor sees an empty page until the cached copy expires or they force a reload.

How to see it: run the image and curl -sI http://127.0.0.1:8080/: the answer has Last-Modified and ETag and no Cache-Control. Then build and run a new image from a changed web/src/main.js on the same port and open the page in a browser that had it open before: the old index.html can be served from cache and its /assets/index-*.js is 404.

What is right: README.md Deployment says the image "Caches static assets with immutable headers"; that only works when the page itself is always revalidated. The usual answer is Cache-Control: no-cache (or max-age=0, must-revalidate) on index.html, so the browser revalidates it with the ETag on every load and the hashed assets stay cached.

Severity: low. It needs a deploy while a visitor holds a cached copy, and a forced reload clears it.

Definition of done

  • index.html (the location / answer, including the try_files fallback) is sent with Cache-Control: no-cache; the /assets/ headers are unchanged. The add_header in that location includes security-headers.conf again, as the /assets/ location does, so the security headers stay on it.
  • A check that requests / from the served config and fails unless Cache-Control is no-cache: in script/frontend-viewport-test (which already serves dist/ with the shipped configs/nginx.conf) or a new test of the nginx config.
  • README.md Deployment says the page is always revalidated and only the assets are cached.
  • TODO.md updated in the same commit; commit title ends (closes #N) with this issue's number.

Model: fable-5-1

`configs/nginx.conf` lines 33 to 35 (`location /`), at `v1.0.0-rc1`: the page (`index.html`) is served with `Last-Modified` and `ETag` and no `Cache-Control`. Browsers then cache it heuristically (a share of its age since `Last-Modified`, which is the build time). The page points at hashed files under `/assets/`, which are served `immutable` and get new names on every build. After a deploy, a browser that still holds the old `index.html` asks for assets the new image no longer has, nginx answers 404, and the visitor sees an empty page until the cached copy expires or they force a reload. **How to see it**: run the image and `curl -sI http://127.0.0.1:8080/`: the answer has `Last-Modified` and `ETag` and no `Cache-Control`. Then build and run a new image from a changed `web/src/main.js` on the same port and open the page in a browser that had it open before: the old `index.html` can be served from cache and its `/assets/index-*.js` is 404. **What is right**: `README.md` Deployment says the image "Caches static assets with immutable headers"; that only works when the page itself is always revalidated. The usual answer is `Cache-Control: no-cache` (or `max-age=0, must-revalidate`) on `index.html`, so the browser revalidates it with the `ETag` on every load and the hashed assets stay cached. **Severity**: low. It needs a deploy while a visitor holds a cached copy, and a forced reload clears it. ## Definition of done - [ ] `index.html` (the `location /` answer, including the `try_files` fallback) is sent with `Cache-Control: no-cache`; the `/assets/` headers are unchanged. The `add_header` in that location includes `security-headers.conf` again, as the `/assets/` location does, so the security headers stay on it. - [ ] A check that requests `/` from the served config and fails unless `Cache-Control` is `no-cache`: in `script/frontend-viewport-test` (which already serves `dist/` with the shipped `configs/nginx.conf`) or a new test of the nginx config. - [ ] `README.md` Deployment says the page is always revalidated and only the assets are cached. - [ ] `TODO.md` updated in the same commit; commit title ends ` (closes #N)` with this issue's number. Model: fable-5-1
clawbot self-assigned this 2026-10-14 16:08:46 +02:00
Author
Collaborator

Plan: in configs/nginx.conf, the location / block sends Cache-Control: no-cache with the page, including the try_files fallback to /index.html, and includes security-headers.conf again, as the /assets/ block does, so the security headers stay on it. The /assets/ headers are unchanged. script/frontend-viewport-test, which already serves dist/ with the shipped configs/nginx.conf, fails unless / and a path that falls back to the page both answer with Cache-Control: no-cache. README.md Deployment says the page is revalidated on every load and only the hashed assets are cached.

Model: opus-5-5

Plan: in `configs/nginx.conf`, the `location /` block sends `Cache-Control: no-cache` with the page, including the `try_files` fallback to `/index.html`, and includes `security-headers.conf` again, as the `/assets/` block does, so the security headers stay on it. The `/assets/` headers are unchanged. `script/frontend-viewport-test`, which already serves `dist/` with the shipped `configs/nginx.conf`, fails unless `/` and a path that falls back to the page both answer with `Cache-Control: no-cache`. `README.md` Deployment says the page is revalidated on every load and only the hashed assets are cached. Model: opus-5-5
Author
Collaborator

Built as planned in #170. The test checks Content-Security-Policy alone for the security headers, since all of them come from the one included file.

Model: opus-5-5

Built as planned in https://git.eeqj.de/sneak/netwatch/pulls/170. The test checks `Content-Security-Policy` alone for the security headers, since all of them come from the one included file. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/netwatch#141