nextjs - ✅(Solved) Fix SSR streaming may emit malformed HTML when document closing tags span multiple chunks [1 pull requests, 1 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#87877Fetched 2026-04-08 02:05:57
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Participants
Timeline (top)
cross-referenced ×1issue_type_added ×1labeled ×1

Error Message

Fix Action

Fixed

PR fix notes

PR #87878: Fix SSR streaming suffix handling when closing tags span chunks

Description (problem / solution / changelog)

What?

Fixes an edge case in the SSR streaming HTML transform where the document closing suffix (</body></html>) can be split across multiple stream chunks and not handled correctly.

The existing implementation only detects the suffix when it appears entirely within a single chunk. This change makes the suffix handling robust to arbitrary chunk boundaries by buffering trailing bytes and matching across chunks.


Why?

During server-side streaming, HTML is emitted incrementally and chunk boundaries are not guaranteed to align with semantic boundaries like document closing tags. When the closing suffix spans multiple chunks, the current logic can emit malformed or incomplete HTML.

Malformed streamed HTML relies on browser error recovery during parsing, which can lead to subtle or undefined hydration behavior (see https://nextjs.org/docs/messages/react-hydration-error). This issue only affects SSR streaming paths and does not occur in fully buffered renders.


How?

  • Introduce a small trailing buffer to retain the last suffix.length - 1 bytes of each chunk.
  • Combine the buffered tail with the next chunk to detect suffixes that span chunk boundaries.
  • Ensure the document closing suffix is emitted exactly once, either when detected in-stream or during flush.

A unit test was added to cover the case where the suffix is split across chunks, which fails on canary and passes with this change.


Tests

  • Added a unit test for createMoveSuffixStream that reproduces the failure when the closing HTML suffix spans multiple stream chunks.

Fixes #87877

Changed files

  • packages/next/src/server/stream-utils/node-web-streams-helper.test.ts (added, +27/-0)
  • packages/next/src/server/stream-utils/node-web-streams-helper.ts (modified, +38/-22)

Code Example

Operating System:
  Platform: linux
  Arch: x64
  Version: #37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2
  Available memory (MB): 23956
  Available CPU cores: 16
Binaries:
  Node: 22.19.0
  npm: 10.9.3
  Yarn: N/A
  pnpm: 9.6.0
Relevant Packages:
  next: 16.1.1-canary.7 // Latest available version is detected (16.1.1-canary.7).
  eslint-config-next: N/A
  react: 19.3.0-canary-65eec428-20251218
  react-dom: 19.3.0-canary-65eec428-20251218
  typescript: 5.9.2
Next.js Config:
  output: N/A
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/vincent-turato/next.js

To Reproduce

  1. Clone the reproduction repository: git clone https://github.com/vincent-turato/next.js.git cd next.js

  2. Check out the reproduction branch: git checkout repro/streaming-html-suffix-bug

  3. Install dependencies: pnpm install

  4. Build the Next.js packages: pnpm build

  5. Run the failing test: pnpm test packages/next/src/server/stream-utils/node-web-streams-helper.test.ts

  6. Observe the failure showing incorrect handling when the closing HTML suffix spans multiple stream chunks:

    FAIL packages/next/src/server/stream-utils/node-web-streams-helper.test.ts createMoveSuffixStream ✕ duplicates closing tags when suffix spans chunks

    ● createMoveSuffixStream › duplicates closing tags when suffix spans Expected: "<html><body>Hello</body></html>" Received: "<html><body>Hello</body></html></body></html>"

Current vs. Expected behavior

Expected: "<html><body>Hello</body></html>" Received: "<html><body>Hello</body></html></body></html>"

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2
  Available memory (MB): 23956
  Available CPU cores: 16
Binaries:
  Node: 22.19.0
  npm: 10.9.3
  Yarn: N/A
  pnpm: 9.6.0
Relevant Packages:
  next: 16.1.1-canary.7 // Latest available version is detected (16.1.1-canary.7).
  eslint-config-next: N/A
  react: 19.3.0-canary-65eec428-20251218
  react-dom: 19.3.0-canary-65eec428-20251218
  typescript: 5.9.2
Next.js Config:
  output: N/A

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

Loading UI and Streaming

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

Vercel (Deployed)

Additional context

Potential impacts of this issue include:

  • Reliance on browser HTML error recovery during parsing, which can lead to subtle or undefined hydration behavior in SSR streaming paths (see https://nextjs.org/docs/messages/react-hydration-error).
  • Emission of malformed or incomplete HTML during server-side streaming when document closing tags span multiple chunks.
  • Missing </body> and/or </html> tags in the streamed response under specific chunking conditions that do not occur in fully buffered renders.

extent analysis

TL;DR

The issue can be fixed by modifying the createMoveSuffixStream function to correctly handle HTML suffixes that span multiple stream chunks.

Guidance

  • Review the createMoveSuffixStream function in node-web-streams-helper.test.ts to identify the logic flaw causing duplicate closing tags.
  • Verify that the function correctly handles edge cases where the HTML suffix is split across multiple chunks.
  • Consider adding additional test cases to cover different chunking scenarios and ensure the function behaves as expected.
  • Check the Next.js documentation and source code for any existing solutions or workarounds for handling HTML suffixes in streaming responses.

Example

No code snippet is provided due to the complexity of the issue and the need for a thorough review of the createMoveSuffixStream function.

Notes

The issue is specific to the createMoveSuffixStream function and its handling of HTML suffixes in streaming responses. The provided test case and error message indicate a clear problem, but a detailed fix requires a thorough understanding of the function's logic and the Next.js streaming implementation.

Recommendation

Apply a workaround by modifying the createMoveSuffixStream function to correctly handle HTML suffixes that span multiple stream chunks, as this is a specific fix for the identified issue.

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 SSR streaming may emit malformed HTML when document closing tags span multiple chunks [1 pull requests, 1 participants]