Skip to content

fix: avoid caching personalized pages#434

Draft
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-investigation-64e4
Draft

fix: avoid caching personalized pages#434
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-investigation-64e4

Conversation

@cursor

@cursor cursor Bot commented Jun 11, 2026

Copy link
Copy Markdown

Summary

  • Prevent the service worker from caching session-personalized / and exact /auth HTML.
  • Respect Cache-Control: private, no-store, and no-cache before writing responses into SW caches.
  • Skip uncacheable paths during native route prefetch and bump the cache version to evict stale personalized page caches.

Bug and impact

A recent service-worker change cached navigation HTML without considering cookies or cache-control headers. This allowed stale signed-out /auth HTML to be served to signed-in users, and stale personalized home HTML (for example, another user's welcome text) to be served from native/PWA page cache on shared installs.

Root cause

/auth did not match the existing /auth/ no-cache prefix, / was preloaded into the page cache, and cache writes ignored response cache-control metadata.

Validation

  • node --check public/sw.js
  • Targeted VM harness verified /, /auth, /auth/callback, and /mod are uncacheable; private/no-store responses are rejected; native prefetch caches /past_papers but skips / and /auth.
  • DATABASE_URL=postgresql://examcooker:examcooker@localhost:5432/examcooker AUTH_SECRET=dev-secret NEXTAUTH_SECRET=dev-secret AUTH_GOOGLE_ID=placeholder AUTH_GOOGLE_SECRET=placeholder NEXTAUTH_URL=http://localhost:3000 NEXT_PUBLIC_BASE_URL=http://localhost:3000 pnpm build
Open in Web View Automation 

Co-authored-by: theg1239 <theg1239@users.noreply.github.com>
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examcooker-dev Error Error Jun 11, 2026 11:12am

@greptile-apps

greptile-apps Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a service-worker bug where personalized HTML pages (/ and /auth) were being cached and could be served stale to different users. It adds exact-path no-cache entries, inspects Cache-Control response headers before every cache write, and bumps the cache version to v4 to evict any already-stored stale entries.

  • NO_CACHE_PATHS set added: / and /auth are now blocked at the isUncacheable check, closing the gap that existed because /auth didn't match the prefix /auth/.
  • isCacheableResponse helper introduced: Replaces the previous response.ok && response.type !== "opaque" check at all three cache-write sites, additionally rejecting responses that carry Cache-Control: private, no-store, or no-cache.
  • PREFETCH_ROUTES handler hardened: Routes are now passed through isUncacheable before fetching, and through isCacheableResponse before storing, preventing the native prefetch path from caching auth or home HTML.

Confidence Score: 4/5

Safe to merge — the fix is targeted and the logic is straightforward, with no changes outside the service worker file.

The exact-path set and the isCacheableResponse helper are correct and well-scoped. The one functional note worth a second look is that no-cache is treated the same as no-store/private, which is more conservative than the HTTP spec requires; this is safe but may silently bypass the stale-while-revalidate benefit for non-personalized routes that use no-cache for revalidation. The version bump and eviction logic are correct.

Only public/sw.js changed — no other files need attention.

Important Files Changed

Filename Overview
public/sw.js Service worker updated to prevent caching personalized / and /auth HTML, add Cache-Control header inspection before writing to any cache, and bump CACHE_VERSION to v4 to evict stale entries.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[fetch event / PREFETCH_ROUTES] --> B{isUncacheable?}
    B -- yes --> C[Skip — browser handles natively]
    B -- no --> D[Fetch from network]
    D --> E{isCacheableResponse?}
    E -- ok + not opaque --> F{Cache-Control header}
    F -- no-store / no-cache / private --> G[Do NOT cache]
    F -- absent or cacheable --> H[Write to STATIC / PAGE / RUNTIME cache]
    E -- not ok or opaque --> G
Loading

Fix All in Codex Fix All in Claude Code Fix All in Cursor

Reviews (1): Last reviewed commit: "fix: avoid caching personalized pages" | Re-trigger Greptile

Comment thread public/sw.js
@@ -1,4 +1,4 @@
const CACHE_VERSION = "v3";
const CACHE_VERSION = "v4";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Commit message could be more descriptive

The message fix: avoid caching personalized pages follows conventional-commit form but is thin — it doesn't mention that /auth exact-match was missing from the no-cache set, that Cache-Control: private/no-store/no-cache is now inspected before every cache write, or that the version bump was needed to evict stale entries. A reader scanning git log won't understand the root cause or the scope of the fix without opening the PR. Something like fix(sw): add /auth+/ exact-no-cache paths, honour Cache-Control private/no-store, bump cache to v4 captures all three changes in one line.

Context Used: Encourage people to write better commit messages (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code Fix in Cursor

Comment thread public/sw.js
Comment on lines +52 to 57
function isCacheableResponse(response) {
if (!response || !response.ok || response.type === "opaque") return false;

const cacheControl = response.headers.get("cache-control") || "";
return !/(^|,\s*)(no-store|no-cache|private)(\s|,|=|$)/i.test(cacheControl);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 no-cache treated as uncacheable — more conservative than HTTP requires

Per RFC 9111, Cache-Control: no-cache means "validate before serving from cache," not "never cache." Treating it as uncacheable here prevents the SW from ever storing or serving such responses, which is safe but more aggressive than necessary. It means any response annotated no-cache (e.g. a public asset served with short-lived revalidation) will always hit the network, bypassing the stale-while-revalidate benefit entirely. Consider whether this is intentional before this becomes a performance concern for non-personalized routes.

Fix in Codex Fix in Claude Code Fix in Cursor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant