Preview iframe serves a stale page from the browser cache after returning to the View module (pass --noHTTPCache to hugo) #342

Closed
opened 2026-10-23 08:42:53 +02:00 by dominik.polakovics · 0 comments

This was generated by AI during triage.

Summary

The preview iframe shows a stale page after the user leaves the View module, saves an element and comes back: the recreated iframe is served from the browser's HTTP cache because hugo server answers pages with Last-Modified and no Cache-Control. Launching Hugo with --noHTTPCache makes it send Cache-Control: no-store, no-cache, must-revalidate, max-age=0, so a recreated iframe always revalidates.

Current behavior

Reported on the hosted editor (site cloonar): "the preview is not reloading when I change things; I have to right-click → This frame → Reload." LiveReload itself is fine: in a standalone tab the /livereload socket stays open and the page reloads on save. The failing flow is inside the editor:

  1. Open the View module for a page (the preview iframe loads /<lang>/<page>/).
  2. Open an element in the Edit module. The View module unmounts; the iframe is destroyed.
  3. Save the element. Hugo rebuilds and serves the new page.
  4. Go back to the View module. A new iframe is created with the same URL.

Reproduced in headless Chromium (CDP) against a local harness serve on the scaffold site: after step 4 the iframe document arrives with fromDiskCache: true and does not contain the saved text, while a direct fetch of the same URL from Hugo already does. Hugo's response headers for a page are Content-Type, Content-Length, Last-Modified, Date, Accept-Ranges — no Cache-Control — so the browser applies heuristic freshness (about 10% of the page's age) and reuses the cached document without revalidating. The hosted control plane and Caddy pass Hugo's headers through unchanged, so the same happens there. Hugo 0.167's server command only sends the no-cache headers when started with --noHTTPCache (its default is off).

Desired behavior

Every page the preview serves is revalidated on each load: a recreated iframe (step 4) shows the saved content without a manual frame reload. LiveReload keeps working as today for an iframe that stays mounted.

Decisions

  • Fix it at the Hugo launch, not in the client — the supervisor is the one owner of Hugo's arguments, and a client-side cache-buster (a query parameter on the iframe URL) would change the URL the pane compares to decide whether to re-point the iframe and would break the "same URL means no reload" discipline in PreviewPane.
  • Pass --noHTTPCache unconditionally (loopback and --preview-base-url alike) — the cache problem is the browser's, not the proxy's.

Key interfaces

  • The Hugo argument list built for hugo server in internal/supervisor (Supervisor.runOnce) — add --noHTTPCache.
  • internal/supervisor/supervisor_test.go asserts the argument list for both launch modes; extend it.
  • docs/harness.md "Serving" names the flags harness serve passes to Hugo (--buildDrafts, the bind, and under "Serving the preview behind a reverse proxy" --appendPort=false / --liveReloadPort); name the new one there. The README gains nothing. No scaffolded skill walks through Hugo's flags (preview-site only runs harness serve), so none changes.

Acceptance criteria

  • hugo server launched by harness serve is passed --noHTTPCache, in loopback mode and with --preview-base-url.
  • A page fetched from the running preview carries Cache-Control: no-store, no-cache, must-revalidate, max-age=0 (and Pragma: no-cache).
  • The supervisor test covers the flag.
  • docs/harness.md "Serving" lists the flag; the README's line budget and link tests still pass.

How to verify

  • go test ./internal/supervisor/ ./docs/ ./cmd/harness/.
  • harness init a scratch site, harness serve --site <dir>, then curl -sI http://127.0.0.1:<preview-port>/ shows the Cache-Control header.
  • The reproduction: in Chromium with DevTools open on the editor, View → Edit an element → save → back to View; the iframe's document request shows "(disk cache)" before the fix and a 200 from the server after it, and the saved text is visible without reloading the frame.

Assumptions to check

  • That Hugo 0.167.0's --noHTTPCache applies to every served page including the in-memory render (it is set in the file server handler, commands/server.go); confirm with the curl above rather than from this brief.

Out of scope

  • Changing when PreviewPane re-points or reloads the iframe (issue #241's discipline stays).
  • The hosted control plane's or Caddy's caching headers for the preview origin — they pass Hugo's through, which is correct once Hugo sends them.
  • The "other language's tree shows translated" report, which is a separate issue.

Blocked by

None - can start immediately

> *This was generated by AI during triage.* ## Summary The preview iframe shows a stale page after the user leaves the View module, saves an element and comes back: the recreated iframe is served from the browser's HTTP cache because `hugo server` answers pages with `Last-Modified` and no `Cache-Control`. Launching Hugo with `--noHTTPCache` makes it send `Cache-Control: no-store, no-cache, must-revalidate, max-age=0`, so a recreated iframe always revalidates. ## Current behavior Reported on the hosted editor (site `cloonar`): "the preview is not reloading when I change things; I have to right-click → This frame → Reload." LiveReload itself is fine: in a standalone tab the `/livereload` socket stays open and the page reloads on save. The failing flow is inside the editor: 1. Open the View module for a page (the preview iframe loads `/<lang>/<page>/`). 2. Open an element in the Edit module. The View module unmounts; the iframe is destroyed. 3. Save the element. Hugo rebuilds and serves the new page. 4. Go back to the View module. A new iframe is created with the same URL. Reproduced in headless Chromium (CDP) against a local `harness serve` on the scaffold site: after step 4 the iframe document arrives with `fromDiskCache: true` and does not contain the saved text, while a direct fetch of the same URL from Hugo already does. Hugo's response headers for a page are `Content-Type`, `Content-Length`, `Last-Modified`, `Date`, `Accept-Ranges` — no `Cache-Control` — so the browser applies heuristic freshness (about 10% of the page's age) and reuses the cached document without revalidating. The hosted control plane and Caddy pass Hugo's headers through unchanged, so the same happens there. Hugo 0.167's `server` command only sends the no-cache headers when started with `--noHTTPCache` (its default is off). ## Desired behavior Every page the preview serves is revalidated on each load: a recreated iframe (step 4) shows the saved content without a manual frame reload. LiveReload keeps working as today for an iframe that stays mounted. ## Decisions - Fix it at the Hugo launch, not in the client — the supervisor is the one owner of Hugo's arguments, and a client-side cache-buster (a query parameter on the iframe URL) would change the URL the pane compares to decide whether to re-point the iframe and would break the "same URL means no reload" discipline in `PreviewPane`. - Pass `--noHTTPCache` unconditionally (loopback and `--preview-base-url` alike) — the cache problem is the browser's, not the proxy's. ## Key interfaces - The Hugo argument list built for `hugo server` in `internal/supervisor` (`Supervisor.runOnce`) — add `--noHTTPCache`. - `internal/supervisor/supervisor_test.go` asserts the argument list for both launch modes; extend it. - `docs/harness.md` "Serving" names the flags `harness serve` passes to Hugo (`--buildDrafts`, the bind, and under "Serving the preview behind a reverse proxy" `--appendPort=false` / `--liveReloadPort`); name the new one there. The README gains nothing. No scaffolded skill walks through Hugo's flags (`preview-site` only runs `harness serve`), so none changes. ## Acceptance criteria - [ ] `hugo server` launched by `harness serve` is passed `--noHTTPCache`, in loopback mode and with `--preview-base-url`. - [ ] A page fetched from the running preview carries `Cache-Control: no-store, no-cache, must-revalidate, max-age=0` (and `Pragma: no-cache`). - [ ] The supervisor test covers the flag. - [ ] `docs/harness.md` "Serving" lists the flag; the README's line budget and link tests still pass. ## How to verify - `go test ./internal/supervisor/ ./docs/ ./cmd/harness/`. - `harness init` a scratch site, `harness serve --site <dir>`, then `curl -sI http://127.0.0.1:<preview-port>/` shows the `Cache-Control` header. - The reproduction: in Chromium with DevTools open on the editor, View → Edit an element → save → back to View; the iframe's document request shows "(disk cache)" before the fix and a 200 from the server after it, and the saved text is visible without reloading the frame. ## Assumptions to check - That Hugo 0.167.0's `--noHTTPCache` applies to every served page including the in-memory render (it is set in the file server handler, `commands/server.go`); confirm with the curl above rather than from this brief. ## Out of scope - Changing when `PreviewPane` re-points or reloads the iframe (issue #241's discipline stays). - The hosted control plane's or Caddy's caching headers for the preview origin — they pass Hugo's through, which is correct once Hugo sends them. - The "other language's tree shows translated" report, which is a separate issue. ## Blocked by None - can start immediately
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle#342
No description provided.