Skip to content

/_next/data/ responses missing Content-Length header since v15.4.1 (Pages Router, self-hosted behind CDN) #90281

@bbrouse

Description

@bbrouse

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ConnectionRelated to the connection() function.HeadersRelated to the async headers() function.Pages RouterRelated to Pages Router.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions