fix: prevent CloudFront cache poisoning for Next.js RSC responses#119
Open
fix: prevent CloudFront cache poisoning for Next.js RSC responses#119
Conversation
1e02670 to
3b6da4f
Compare
Add a CloudFront Function (VIEWER_REQUEST) that hashes Next.js RSC headers (rsc, next-router-prefetch, next-router-state-tree, next-router-segment-prefetch, next-url) into a single x-nextjs-cache-key header included in the Cache Policy. This approach avoids CloudFront's 10-header limit on Cache Policies (currently 5 used + 1 added = 6/10) while correctly separating cache entries for HTML vs RSC flight responses. Closes #100
3b6da4f to
36e0905
Compare
Contributor
Author
VerificationDeployed and verified with a temporary static page ( curlNo cross-contamination: HTML requests never received RSC payloads, and vice versa. playwright-cli (browser)
|
Contributor
Author
|
@tmokmss ヘッダー数が多くなったのでcdk-nextjsを参考にCF Functionで実装しました。確認いただけますか? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Prevent CloudFront cache poisoning between HTML and RSC flight responses by adding a CloudFront Function that hashes Next.js RSC headers into a single cache key header.
Closes #100
Supersedes #118
Problem
Next.js App Router sends two types of requests to the same URL:
RSC: 1header, returningtext/x-componentflight dataNext.js sets
Vary: rsc, next-router-state-tree, next-router-prefetch, next-router-segment-prefetch(plusnext-urlfor interception routes) to signal that responses differ based on these headers. However, CloudFront does not honorVary— headers must be explicitly included in the Cache Policy to become part of the cache key.Without this fix, when CloudFront caching is active (static pages, ISR, or explicit cache headers), an RSC response can be cached and served for a normal HTML request, or vice versa.
Approach
Adding all 5 RSC headers directly to the Cache Policy would hit CloudFront's 10-header limit (5 existing + 5 = 10, no room for future additions). Instead, we use a CloudFront Function (VIEWER_REQUEST) that:
rsc,next-router-prefetch,next-router-state-tree,next-router-segment-prefetch,next-url)x-nextjs-cache-keyheader using FNV-1ax-nextjs-cache-key(6/10 headers used)This is the same approach used by cdk-nextjs (which hashes into
x-open-next-cache-key).Why CloudFront Function (not Lambda@Edge)?
The existing
sign-payloadLambda@Edge (ORIGIN_REQUEST) handles request body hashing for SigV4, which requires body access — only possible with Lambda@Edge. The RSC header hashing is a lightweight header-only operation ideal for CloudFront Functions. Both coexist on the same behavior (CF Function at VIEWER_REQUEST, L@E at ORIGIN_REQUEST).Files changed
cdk/lib/constructs/cf-lambda-furl-service/cf-function/cache-key.js— New CloudFront Functioncdk/lib/constructs/cf-lambda-furl-service/service.ts— Wire up CF Function + addx-nextjs-cache-keyto Cache PolicyGrounding / References
base-server.js:setVaryHeader()— ConfirmsVaryincludesrsc,next-router-state-tree,next-router-prefetch,next-router-segment-prefetchfor all App Router pages, plusnext-urlfor interception routesapp-render.js:149—next-router-segment-prefetch: /_treetriggers a different response (route tree only), confirming it must be in the cache keyVaryheader in Next.js 15.3.0–15.3.3. Workaround: manually setVary: RSC, Next-Router-State-Tree, Next-Router-PrefetchNextjsDistribution.ts— Uses the same hash-into-single-header approach withx-open-next-cache-key