Skip to content
Docs

Do Vercel Functions support WebSocket connections?

Vercel Functions serve WebSocket connections natively, in public beta on all plans. Learn how connections pin to instances, how to handle reconnects, and where to keep shared state.

Vercel

Vercel Functions serve WebSocket connections natively, so a client and your server-side code can exchange messages in both directions over one open connection. WebSocket support is in public beta on all plans and runs on Fluid Compute. Use it for realtime features such as interactive AI streaming, chat, multiplayer state, and collaborative editing. There's no separate socket server to run, because the WebSocket endpoint is itself a Vercel Function.

Copy link to headingOverview

In this guide, you'll learn:

  • How WebSocket connections behave on Vercel Functions
  • When to use Server-Sent Events (SSE) instead of WebSockets
  • How to set up a WebSocket endpoint with ws, Socket.IO, or your framework
  • How to handle reconnects and share state across function instances
  • Which limits and pricing apply to WebSocket connections

Copy link to headingHow WebSockets work on Vercel Functions

A WebSocket connection on Vercel starts as an HTTP GET request with an Upgrade header, and that request passes through the same controls as any other request to a Vercel Function. Routing Middleware, rewrites, Firewall rules, and rate limits all run before the upgrade completes. You can write Firewall rules that target the WebSocket request path, and rate limits apply to each upgrade request.

After the upgrade succeeds, three behaviors shape how a WebSocket application on Vercel works:

  • Pinning: A connection stays on the function instance that accepted it for the connection's whole lifetime. Fluid Compute lets one instance hold many connections at once.
  • Placement: A new connection can reach any instance, including when a client reconnects. After a deployment, new connections may reach the new deployment while existing connections stay on the previous deployment until they close.
  • Duration: A connection closes when the function reaches its maximum duration, so every client needs reconnect logic.

Copy link to headingWhen to choose SSE over WebSockets on Vercel

Use Server-Sent Events (SSE) when data only flows from the server to the client, and use WebSockets when the client also sends a continuous stream of its own. Both run on Vercel Functions.

AI response streaming is the most common one-way case. A model generates tokens, the server sends them as they arrive, and the client submits its next prompt as a separate HTTP request. SSE delivers this over a standard HTTP response, and the browser's EventSource API reconnects automatically after a drop. On reconnect, the browser sends a Last-Event-ID header so your server can resume the stream. SSE streams are still bounded by the function's maximum duration, but the browser handles reconnection for you, while WebSocket reconnection and state resync are code you write yourself.

Choose WebSockets when the client pushes data continuously without waiting on a new request, as in multiplayer game state, collaborative editing, typing indicators, and live cursors. For a deeper comparison of the two protocols, see WebSocket vs Server-Sent Events.

Copy link to headingHow to set up a WebSocket endpoint

WebSockets require Fluid Compute, which is enabled by default for projects created on or after April 23, 2025. For an older project, turn it on in your project's Settings under Functions, then redeploy.

WebSockets in Vercel Functions behave like any distributed WebSocket server, so standard libraries work without extra configuration. With ws, create a Node.js HTTP server, attach a WebSocketServer, and export the HTTP server as the default export:

api/ws.ts
import http from 'http';
import { WebSocketServer } from 'ws';
const server = http.createServer();
const wss = new WebSocketServer({ server });
wss.on('connection', (ws) => {
ws.on('message', (data) => {
ws.send(data);
});
});
export default server;

This example echoes each message back to the client that sent it, at wss://your-domain.com/api/ws.

Socket.IO works the same way on the server, but the Socket.IO client defaults to HTTP long-polling, so configure it to use the WebSocket transport directly. Create the server in api/socket-io.ts:

api/socket-io.ts
import http from 'http';
import { Server } from 'socket.io';
const server = http.createServer();
const io = new Server(server);
io.on('connection', (socket) => {
socket.on('message', (data) => {
socket.send(data);
});
});
export default server;

Then point the client at the full Socket.IO path and set transports to ['websocket']:

client.ts
import { io } from 'socket.io-client';
const socket = io('https://your-domain.com', {
// Socket.IO appends /socket.io to the path by default,
// so the full path becomes /api/socket-io/socket.io
path: '/api/socket-io/socket.io',
transports: ['websocket'],
});

Python applications can use python-socketio, and Flask applications can use Flask-SocketIO. Both libraries implement the Socket.IO protocol, so clients need a compatible Socket.IO client.

Copy link to headingWhich frameworks support WebSockets on Vercel

Frameworks with native WebSocket support serve connections on Vercel Functions without Vercel-specific configuration. Each framework below has a full example in the Use with frameworks section of the WebSockets documentation.

  • Node.js server frameworks: Express, Hono, and h3 serve connections through ws or the framework's own upgrade helper, with the server exported as the default export of the function file, such as api/server.ts.
  • Bun: The Bun runtime supports the native Bun.serve() WebSocket API. Call server.upgrade() from the fetch handler, then define handlers in the websocket option.
  • Nitro and Nuxt: Nitro's WebSocket support is powered by crossws. Enable features.websocket in your Nitro config, then export a handler from defineWebSocketHandler() in a route file. The Nitro and Nuxt starter templates deploy without changes.
  • Python: Applications built on the Asynchronous Server Gateway Interface (ASGI) or Web Server Gateway Interface (WSGI) serve connections without a Vercel-specific upgrade API. Use FastAPI's @app.websocket() decorator, Django Channels, or flask-sock for Flask.
  • Next.js: Next.js has no built-in API for WebSocket upgrades, so @vercel/functions provides experimental_upgradeWebSocket(). Return it from the GET export of a route handler.

Because Next.js is the one framework that needs a Vercel-specific API, here is the full route handler:

app/api/ws/route.ts
import {
experimental_upgradeWebSocket,
type WebSocketData,
} from '@vercel/functions';
export async function GET() {
return experimental_upgradeWebSocket((ws) => {
ws.on('message', (data: WebSocketData) => {
ws.send(data);
});
});
}

See the experimental_upgradeWebSocket() reference for the full API.

Copy link to headingHow to handle reconnects and shared state for WebSocket connections

WebSocket applications on Vercel need two pieces of client and server logic beyond the endpoint itself, because connections close at the maximum duration and a reconnecting client can land on any instance.

Copy link to headingHow to reconnect when a connection closes

Every WebSocket connection on Vercel closes when the function reaches its maximum duration, so treat a close as an expected event rather than an error. Reconnect logic should recreate the connection, resubscribe to any channels or topics, and reload the state the client needs to continue.

The following client reconnects with exponential backoff, which keeps it from retrying in a tight loop while the endpoint is unavailable:

client.ts
let socket: WebSocket;
let reconnectDelay = 1000;
function connect() {
socket = new WebSocket('wss://your-domain.com/api/ws');
socket.addEventListener('open', () => {
reconnectDelay = 1000;
});
socket.addEventListener('message', (event) => {
console.log(event.data);
});
socket.addEventListener('close', () => {
setTimeout(connect, reconnectDelay);
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
});
}
connect();

The reconnect delay starts at 1 second, doubles after each close up to 30 seconds, and resets to 1 second once a connection opens. If many clients tend to connect at the same moment, add a random offset to the delay so their reconnects spread out.

Copy link to headingHow to share state across function instances

New WebSocket connections aren't guaranteed to reach the same function instance, and in-memory variables only reach the connections on the instance that holds them. Without a shared store, two clients in the same chat room can connect to different instances and never see each other's messages. Built-in broadcast helpers have the same boundary, so Bun's server.publish() only reaches sockets on the same instance.

Keep durable state, presence, counters, rooms, and pub/sub coordination in an external data store. Redis from the Vercel Marketplace is reachable from every instance and every deployment. In a chat app, each instance publishes incoming messages to a Redis channel and subscribes to that channel, so a message from a user on one instance reaches users connected to another. Publish and subscribe to realtime data on Vercel compares the options for realtime fan-out.

Copy link to headingWebSocket limits and pricing on Vercel

WebSocket connections run on Vercel Functions and follow the same limits and pricing model as any other function invocation. The function's maximum duration sets how long a single connection can stay open:

PlanDefaultMaximumExtended maximum (beta)
Hobby300s300sNot available
Pro300s800s1,800s
Enterprise300s800s1,800s

The extended maximum is available on the Node.js, Bun, and Python runtimes. Set durations above 800s per function, with maxDuration in vercel.json or in a Next.js route file, rather than as a project default. See Configuring maximum duration for each method.

Vercel Functions on Fluid Compute bill for Active CPU, Provisioned Memory, and Invocations. Active CPU applies only while your code processes messages, so an idle connection doesn't accrue CPU time. Provisioned Memory bills for the lifetime of the instance, which means an open connection keeps its instance's memory billable until the connection closes. One instance holds many connections, so that memory cost is shared across every connection on the instance. Data sent over the connection counts toward Fast Data Transfer and Fast Origin Transfer.

Copy link to headingNext steps

Deploy the Nitro WebSockets starter to begin from working code, or start a new project and add a WebSocket endpoint with the setup above.

Copy link to headingFrequently asked questions

Copy link to headingAre WebSockets on Vercel still in beta?

Yes. WebSocket support for Vercel Functions entered public beta on June 22, 2026, and Python support followed on July 23, 2026. Behavior and APIs may change during the beta. WebSockets are available on all plans and require Fluid Compute, which is enabled by default for projects created on or after April 23, 2025.

Copy link to headingHow long can a WebSocket connection stay open on Vercel?

A WebSocket connection stays open until the function reaches its maximum duration. That's 300s on Hobby. On Pro and Enterprise, the default is 300s, you can configure up to 800s, and an extended maximum of 1,800s is in beta for Node.js, Bun, and Python functions. Every connection eventually closes, so add reconnect logic to your client.

Copy link to headingDo I need a separate WebSocket server to use WebSockets on Vercel?

No. The WebSocket endpoint is a Vercel Function, so there's no standalone socket server to provision or scale. Standard libraries such as ws and Socket.IO work without extra configuration, and one function instance handles many connections at once. To share state across instances, use an external store such as Redis.

Copy link to headingHow much do WebSocket connections cost on Vercel?

WebSocket connections follow the standard Vercel Functions pricing model on Fluid Compute, billed on Active CPU, Provisioned Memory, and Invocations. Active CPU applies only while your code processes messages, not while the connection sits idle. Provisioned Memory bills for the instance lifetime, so an open connection keeps its instance billable until it closes.

Copy link to headingDo WebSockets work with Next.js on Vercel?

Yes. Next.js doesn't expose an API for WebSocket upgrades, so use experimental_upgradeWebSocket() from @vercel/functions inside a route handler. Frameworks with native WebSocket support, including Express, Hono, Nitro, and FastAPI, serve connections without a Vercel-specific API.

Related documentation

More Vercel Functions guides