nextjs - ✅(Solved) Fix `/_next/data/` responses missing Content-Length header since v15.4.1 (Pages Router, self-hosted behind CDN) [1 pull requests, 8 comments, 4 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#90281Fetched 2026-04-08 00:20:28
View on GitHub
Comments
8
Participants
4
Timeline
18
Reactions
1
Author
Timeline (top)
commented ×8labeled ×4referenced ×2closed ×1

Fix Action

Fix / Workaround

  1. Clone https://github.com/bbrouse/nextjs-content-length-repro
  2. cd after && npm install && npx next build && npx next start -p 4200
  3. curl -sD - -o /dev/null "http://localhost:4200/_next/data/$(cat .next/BUILD_ID)/index.json"
  4. Observe Transfer-Encoding: chunked with no Content-Length or ETag
  5. Repeat with the before/ directory (v15.4.0) -- Content-Length and ETag are present
  6. Repeat with after-patched/ directory (v15.4.1 patched) -- Content-Length and ETag are restored

We verified this locally by patching the ESM template to remove Buffer.from():

  - new RenderResult(Buffer.from(JSON.stringify(result.value.pageData)), {
  + new RenderResult(JSON.stringify(result.value.pageData), {

After rebuilding, Content-Length and ETag are restored. The after-patched/ directory in the reproduction repo automates this test.

PR fix notes

PR #90304: Fix(pages-router): restore Content-Length and ETag for /_next/data/ JSON responses

Description (problem / solution / changelog)

What

Removes the Buffer.from() wrapper when constructing RenderResult for /_next/data/ JSON responses in the Pages Router handler.

Why

PR #80189 introduced Buffer.from(JSON.stringify(result.value.pageData)) when building the data response. Since RenderResult.isDynamic checks typeof this.response !== 'string', passing a Buffer (not a string) caused it to return true, making sendRenderResult treat the response as a dynamic stream — skipping Content-Length and ETag generation and falling back to Transfer-Encoding: chunked.

This is a regression from v15.4.0 and breaks CDN-side compression for self-hosted deployments (e.g. CloudFront requires Content-Length to compress origin responses on-the-fly).

Fix

- Buffer.from(JSON.stringify(result.value.pageData)),
+ JSON.stringify(result.value.pageData),

Testing

Affected Area

  • Pages Router — /_next/data/ responses only
  • No impact on App Router
  • Single-line change, minimal blast radius

Fixes #90281

Changed files

  • AGENTS.md (modified, +4/-4)
  • packages/next/src/server/route-modules/pages/pages-handler.ts (modified, +11/-17)
  • test/e2e/prerender.test.ts (modified, +16/-4)

Code Example

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.5.0: Wed May  1 20:13:18 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T6030
  Available memory (MB): 36864
  Available CPU cores: 12
Binaries:
  Node: 24.13.0
  npm: 6.14.18
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 15.3.9
  eslint-config-next: 15.5.2
  react: 18.3.1
  react-dom: 18.3.1
Next.js Config:
  output: N/A

---

new RenderResult(Buffer.from(JSON.stringify(result.value.pageData)), { ... })

---

RenderResult.fromStatic(JSON.stringify(cachedData.pageData))

---

- new RenderResult(Buffer.from(JSON.stringify(result.value.pageData)), {
  + new RenderResult(JSON.stringify(result.value.pageData), {
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/bbrouse/nextjs-content-length-repro

To Reproduce

The (admittedly vibe coded) reproduction tests can be run as follows:

  1. Clone https://github.com/bbrouse/nextjs-content-length-repro
  2. cd after && npm install && npx next build && npx next start -p 4200
  3. curl -sD - -o /dev/null "http://localhost:4200/_next/data/$(cat .next/BUILD_ID)/index.json"
  4. Observe Transfer-Encoding: chunked with no Content-Length or ETag
  5. Repeat with the before/ directory (v15.4.0) -- Content-Length and ETag are present
  6. Repeat with after-patched/ directory (v15.4.1 patched) -- Content-Length and ETag are restored

Current vs. Expected behavior

We run a self-hosted Next.js Pages Router application on AWS ECS behind cloudfront. We leverage cloudfronts ability to compress content on the fly, so next.js compression is disabled.

After upgrading from v15.4.0 to v15.4.1, we noticed that /_next/data/ JSON responses (from getStaticProps pages with revalidate) were no longer gzipped by cloudfront (the initial indicator being a noticeable increase in data transfer over cloudfront).

The cause: these responses no longer include a Content-Length header. Instead they are streams that send Transfer-Encoding: chunked. CloudFront requires Content-Length to compress origin responses, so the JSON payloads are now served uncompressed.

Expected: /_next/data/ responses for cached/ISR pages include Content-Length and ETag, matching the behavior in v15.4.0 and earlier.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.5.0: Wed May  1 20:13:18 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T6030
  Available memory (MB): 36864
  Available CPU cores: 12
Binaries:
  Node: 24.13.0
  npm: 6.14.18
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 15.3.9
  eslint-config-next: 15.5.2
  react: 18.3.1
  react-dom: 18.3.1
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Pages Router, Connection, Headers

Which stage(s) are affected? (Select all that apply)

Other (Deployed), next start (local)

Additional context

Debugging on our end:

We (and by we I mean me and my helpful robot friends) traced this to PR #80189 ("Add response handling inside handlers"), which moved response handling into the Pages route handler template. The new code in packages/next/src/build/templates/pages.ts constructs the data response as:

  new RenderResult(Buffer.from(JSON.stringify(result.value.pageData)), { ... })

The previous code in base-server.ts used:

  RenderResult.fromStatic(JSON.stringify(cachedData.pageData))

The difference is that Buffer.from() is not a string, which makes RenderResult.isDynamic return true (it checks typeof this.response !== 'string'). This causes sendRenderResult to skip Content-Length and ETag generation and pipe the response instead.

We verified this locally by patching the ESM template to remove Buffer.from():

  - new RenderResult(Buffer.from(JSON.stringify(result.value.pageData)), {
  + new RenderResult(JSON.stringify(result.value.pageData), {

After rebuilding, Content-Length and ETag are restored. The after-patched/ directory in the reproduction repo automates this test.

Additional context of our application:

We run a self-hosted Next.js Pages Router site on AWS ECS behind CloudFront. Our Docker image is based on node:24-alpine3.23 and we use a custom Express server with compress: false, since CloudFront handles compression at the edge and we're happy to offload those cycles off the containers to cloudfront.

extent analysis

Fix Plan

Patch the ESM template

  1. Update packages/next/src/build/templates/pages.ts:
    - new RenderResult(Buffer.from(JSON.stringify(result.value.pageData)), { ... })
    + new RenderResult(JSON.stringify(result.value.pageData), { ... })
  2. Rebuild Next.js:
    cd packages/next
    npm install && npm run build
  3. Update your Next.js project:
    npm install next@latest
  4. Verify the fix: Run the reproduction tests again. The /_next/data/ responses should now include Content-Length and ETag headers.

Verification

  1. Run the reproduction tests:
    curl -sD - -o /dev/null "http://localhost:4200/_next/data/$(cat .next/BUILD_ID)/index.json"
  2. Check the response headers:
    curl -s -I "http://localhost:4200/_next/data/$(cat .next/BUILD_ID)/index.json"
    Look for Content-Length and ETag headers.

Extra Tips

  • Make sure to update your Next.js project to the latest version.
  • If you're using a custom Express server, ensure that compress is set to false to avoid conflicts with CloudFront compression.
  • Consider opening an issue on the Next.js GitHub repository to discuss this fix and potential future improvements.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

nextjs - ✅(Solved) Fix `/_next/data/` responses missing Content-Length header since v15.4.1 (Pages Router, self-hosted behind CDN) [1 pull requests, 8 comments, 4 participants]