Skip to content

fix(use-cache): fix HMR not updating in cross-origin iframes#90198

Open
lllomh wants to merge 6 commits intovercel:canaryfrom
lllomh:fix/use-cache-hmr-iframe
Open

fix(use-cache): fix HMR not updating in cross-origin iframes#90198
lllomh wants to merge 6 commits intovercel:canaryfrom
lllomh:fix/use-cache-hmr-iframe

Conversation

@lllomh
Copy link

@lllomh lllomh commented Feb 19, 2026

Summary

When a Next.js app is embedded in a cross-origin iframe (e.g. behind an
HTTPS tunnel like cloudflared or ngrok), "use cache" components would
not update during HMR. A hard refresh was required to see changes.

Fixes #90143

Root Cause

HMR cache-busting relies on a __next_hmr_refresh_hash__ cookie being
sent back to the server with each HMR refresh request. The server includes
this hash in the cache key of all "use cache" functions, allowing stale
entries to be bypassed.

In a cross-origin iframe context, the cookie is never sent:

  1. SameSite=Lax (the browser default): cookies are blocked when the
    top-level context is a different site from the iframe, even for
    same-origin fetch requests made by the iframe's own JavaScript.
  2. "Block third-party cookies": browsers may block the cookie from
    being set or read entirely in cross-site iframe contexts.

Without the hash, the server returns the old cached value. Because this is
a server-side cache, the stale result is returned to all clients — which
explains why even a completely different browser would see outdated content.

Fix

Two-layer approach:

1. SameSite=None cookie on HTTPS
When the dev server is served over HTTPS, the cookie is now set with
SameSite=None; Secure, which allows it to be transmitted in cross-origin
requests. This fixes the common tunnel setup (cloudflared, ngrok, etc.)
for browsers that allow third-party cookies.

2. Request header fallback
The current hash is also stored in a module-level variable
(hmr-client-state.ts) and sent as a next-hmr-refresh-hash request
header on every HMR refresh fetch. The server now checks this header as a
fallback when the cookie is absent. This covers environments where
third-party cookies are blocked entirely.

Changes

  • app-router-headers.ts — add NEXT_HMR_REFRESH_HASH_HEADER constant
  • hmr-client-state.ts — new shared module to hold the current hash
    client-side
  • hot-reloader-app.tsx — set SameSite=None;Secure on HTTPS; store
    hash in shared state
  • fetch-server-response.ts — include hash as next-hmr-refresh-hash
    header when isHmrRefresh is true
  • work-unit-async-storage.external.ts — fall back to the header in
    getHmrRefreshHash when the cookie is not present

@nextjs-bot
Copy link
Collaborator

Allow CI Workflow Run

  • approve CI run for commit: 11c9c92

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

1 similar comment
@nextjs-bot
Copy link
Collaborator

Allow CI Workflow Run

  • approve CI run for commit: 11c9c92

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"use cache"'d components do not update during HMR inside an iFrame

2 participants

Comments