Vercel Functions move to streaming by default
Vercel is turning on HTTP response streaming for all Node.js Functions. The capability has been available since 2023, but adoption has been selective: frameworks such as Next.js (App Router), SvelteKit, and Remix have used it, while others continued to buffer responses. That distinction is going away as streaming rolls out to every function and compatible framework.
Streaming lets your server send the first bytes of a response—whether an API payload or server-rendered template—to the client before the function finishes executing. Users see content sooner, and the function itself can release the connection once it has completed writing.
Error behavior changes under streaming
A buffered function that throws a runtime error mid-execution has one clean outcome: the client receives a 500 after the function exits. Streaming changes that contract because headers are flushed with the initial chunk of the response body.
Consider a function that writes to the response after a delay and then throws:
import { setTimeout } from "node:timers/promises";
export default async function (req, res) {
res.writeHead(200, { "Content-Type": "text/plain" });
res.write("Chunk 1\\n");
await setTimeout(1000);
throw new Error("Oh no!");
}
Without streaming, this request would hang for several seconds before returning a 500:
HTTP/2 500
cache-control: public, max-age=0, must-revalidate
content-type: text/plain; charset=utf-8
date: Mon, 24 Jun 2024 10:26:35 GMT
server: Vercel
strict-transport-security: max-age=63072000; includeSubDomains; preload
x-robots-tag: noindex
x-vercel-error: FUNCTION_INVOCATION_FAILED
x-vercel-id: cdg1::hgz6d-1719224792404-c4031128df3b
content-length: 56
A server error has occurred
FUNCTION_INVOCATION_FAILED
With streaming enabled, the status code and partial body have already reached the client by the time the error occurs. The stream ends, and the error e.g., Oh no! lands in the Vercel logs, but the client has already received what was sent, resulting in a 200 with truncated content:
HTTP/2 200
age: 0
cache-control: public, max-age=0, must-revalidate
content-type: text/plain
date: Mon, 24 Jun 2024 10:29:58 GMT
server: Vercel
strict-transport-security: max-age=63072000; includeSubDomains; preload
x-robots-tag: noindex
x-vercel-cache: MISS
x-vercel-execution-region: iad1
x-vercel-id: cdg1::iad1::zclv9-1719224997952-452256b220b0
Chunk 1
Logs become granular and real-time
Buffering imposes constraints on observability too. Traditionally, logs from a non-streaming function are held until the invocation resolves, then delivered as one collapsed entry with all context for that call, up to a 4KB size limit in the Logs tab. A function that writes incrementally with delays demonstrates this:
import { setTimeout } from "node:timers/promises";
export default async function (req, res) {
console.log("Before setting a response");
res.writeHead(200, { "Content-Type": "text/plain" });
res.write("Chunk 1\\n");
await setTimeout(1000);
console.warn("Before writing a second chunk");
res.write("Chunk 2\\n");
await setTimeout(1000);
console.error("Before ending the response");
res.end("Done!");
}
Calling it three times yields three log lines, each only visible after the full invocation completes:
Streaming changes the frequency and shape entirely:
Each console.log statement now appears on its own line with a precise timestamp, arriving near real-time without waiting for the invocation to finish. One consequence: individual log lines don't carry a status code, since logs can be emitted before a status is decided. With streaming on by default, this behavior becomes uniform across all Vercel Functions.
This shift also affects Vercel Log Drains. If you consume logs through that channel, verify that your provider can handle streaming responses and the higher frequency of log events.
Opting in before the rollout
Hobby accounts will see streaming enabled by default beginning July 8th, 2024. Pro and Enterprise accounts follow on October 1st, 2024.
To activate streaming as the default immediately, set the VERCEL_FORCE_NODEJS_STREAMING environment variable to true for your project. The change takes effect with your next deployment.



