WebSocket Alternatives for Vercel and Cloudflare Workers
Vercel and Cloudflare Workers don't support WebSockets natively. Here are your options for adding real-time features to serverless apps, from polling to managed services.
You chose serverless for good reasons: zero infrastructure management, automatic scaling, pay-per-use pricing, and instant global deployment. Vercel and Cloudflare Workers deliver on all of this.
Then you tried to add real-time features and discovered the catch: WebSockets don't work.
This isn't a bug — it's a fundamental limitation of the serverless model. But it doesn't mean you're stuck. In this guide, I'll walk through every option for adding real-time functionality to your Vercel or Cloudflare Workers app, with honest pros and cons for each.
Why WebSockets Don't Work on Serverless
First, let's understand the problem. WebSockets require:
- Persistent connections — A TCP connection that stays open indefinitely
- Stateful servers — The server must "remember" which clients are connected
- Bidirectional communication — Both client and server can send messages at any time
Serverless functions are the opposite:
- Request/response only — Function starts, processes request, returns response, terminates
- Stateless — No memory between invocations
- Timeout limits — Vercel functions max out at 60 seconds (Pro) or 300 seconds (Enterprise)
A WebSocket connection that needs to stay open for hours simply cannot exist in a function that terminates after seconds.
Option 1: Polling (The "Works But..." Option)
The simplest approach: have clients repeatedly ask "anything new?"
// Client-side polling
setInterval(async () => {
const response = await fetch('/api/updates');
const updates = await response.json();
if (updates.length > 0) {
handleUpdates(updates);
}
}, 5000); // Check every 5 seconds
Pros:
- Works everywhere, no special infrastructure
- Simple to implement
- No third-party dependencies
Cons:
- High latency — 5-second polling means 2.5 seconds average delay
- Wasteful — Most requests return nothing
- Expensive at scale — 10,000 users × 12 requests/minute = 7.2 million requests/hour
- Poor UX — Users notice the delay
Verdict: Acceptable for low-frequency updates (checking order status every minute), but not viable for chat, notifications, or live collaboration.
Option 2: Long Polling (Polling's Smarter Cousin)
Long polling keeps the request open until there's data to return:
// Client-side long polling
async function longPoll() {
try {
const response = await fetch('/api/updates?wait=true');
const updates = await response.json();
handleUpdates(updates);
} catch (error) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
longPoll(); // Immediately start next request
}
Pros:
- Near-instant delivery when updates occur
- More efficient than regular polling
- Still works with standard HTTP
Cons:
- Timeout issues — Serverless functions timeout; you need to return before the limit
- Connection overhead — Still creating new connections frequently
- Complex state management — Server needs to track pending requests
- Vercel limitation — Max 60-second hold means forced reconnection cycles
Verdict: Better than polling, but the timeout limits make it awkward on serverless platforms.
Option 3: Server-Sent Events (SSE)
SSE is a one-way streaming protocol. The server can push events to the client over a single HTTP connection:
// API route (Vercel Edge Function)
export const config = { runtime: 'edge' };
export default async function handler(req) {
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
// Send an event every second
for (let i = 0; i < 30; i++) {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({ count: i })}\n\n`)
);
await new Promise(r => setTimeout(r, 1000));
}
controller.close();
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
});
}
Pros:
- Native browser support (EventSource API)
- Works with Edge Functions (longer timeout than serverless)
- Lower overhead than WebSockets
- Automatic reconnection built into the protocol
Cons:
- One-way only — Server to client; client-to-server still needs HTTP requests
- Still has timeouts — Edge functions eventually timeout too
- No state coordination — Multiple function instances don't share connected clients
- Can't broadcast — Each connection is isolated
Verdict: Good for streaming responses (AI chat completions), but not a full WebSocket replacement. You can't easily broadcast "user X just updated the document" to all other users.
Option 4: Third-Party Pub/Sub Services
This is where managed real-time services come in. The architecture:
- Clients connect to the third-party service via WebSocket
- Your serverless functions publish messages via REST API
- The service broadcasts messages to connected clients
Comparing the Options
| Service | Free Tier | Paid Starting | Best For |
|---|---|---|---|
| PushFlo | 500K msg/mo | $19/mo | Indie devs, small teams |
| Pusher | 200K msg/day | $49/mo | Established startups |
| Ably | 6M msg/mo | $29/mo | Enterprise features |
| Firebase RTDB | Spark plan | $25/mo+ | Google ecosystem |
| Supabase Realtime | 500 connections | Usage-based | Postgres users |
For Vercel deployments specifically, see our dedicated WebSockets for Vercel guide. If you're using other serverless platforms, check out Serverless WebSockets.
PushFlo Example (Vercel)
// Client: Subscribe to updates
import { PushFloClient } from '@pushflodev/sdk';
const client = new PushFloClient({ publishKey: 'your-publish-key' });
client.connect();
client.subscribe('notifications', {
onMessage: (message) => {
showNotification(message);
},
});
// Server: Publish updates (Vercel API Route)
export async function POST(request) {
// Do your business logic...
// Then notify clients
await fetch('https://api.pushflo.dev/api/v1/publish', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PUSHFLO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
channel: 'notifications',
event: 'new',
data: { message: 'Something happened!' }
}),
});
return Response.json({ success: true });
}
Pros:
- True real-time — Sub-50ms latency
- No infrastructure — Someone else runs the WebSocket servers
- Scales automatically — From 1 to millions of connections
- Works with any serverless platform — Vercel, Cloudflare, Netlify, AWS Lambda
Cons:
- Third-party dependency — Your real-time features depend on their uptime
- Cost at scale — High-volume apps pay more (though still cheaper than self-hosting)
Verdict: The practical choice for most serverless apps. You get real WebSocket functionality without compromising your serverless architecture.
Option 5: Cloudflare Durable Objects (Cloudflare-Specific)
If you're all-in on Cloudflare, Durable Objects offer a unique solution. They're stateful, single-threaded "actors" that can hold WebSocket connections:
// Durable Object class
export class ChatRoom {
constructor(state, env) {
this.state = state;
this.sessions = [];
}
async fetch(request) {
const upgradeHeader = request.headers.get('Upgrade');
if (upgradeHeader === 'websocket') {
const pair = new WebSocketPair();
this.sessions.push(pair[1]);
pair[1].accept();
pair[1].addEventListener('message', (msg) => {
this.broadcast(msg.data);
});
return new Response(null, { status: 101, webSocket: pair[0] });
}
return new Response('Expected WebSocket', { status: 400 });
}
broadcast(message) {
this.sessions.forEach(ws => ws.send(message));
}
}
Pros:
- True WebSockets — Native support within Cloudflare's edge network
- Stateful — Objects persist and maintain state
- Global distribution — Runs at the edge, close to users
Cons:
- Cloudflare lock-in — Only works on Cloudflare Workers
- Learning curve — Different programming model than traditional serverless
- Complexity — You're managing WebSocket logic yourself
- Pricing — Durable Objects have their own pricing tier
Verdict: Powerful if you're committed to Cloudflare and willing to invest in learning the model. Overkill for simple notification features.
Decision Framework: Which Option Should You Choose?
Use this flowchart:
Is sub-second latency critical?
├── No → Consider Polling or Long Polling
└── Yes ↓
Are you Cloudflare-only and okay with vendor lock-in?
├── Yes → Consider Durable Objects
└── No ↓
Do you need full control over WebSocket logic?
├── Yes → Hybrid architecture (self-hosted)
└── No ↓
Use a managed pub/sub service (PushFlo, Pusher, Ably)
For most developers building on Vercel or Cloudflare Workers, a managed pub/sub service is the sweet spot:
- Real WebSocket functionality
- Zero infrastructure to manage
- Works with your existing serverless setup
- Free tiers to get started
Quick Start with PushFlo
Here's the minimal code to add real-time to your Vercel or Cloudflare Workers app:
1. Client-side (any framework):
import { PushFloClient } from '@pushflodev/sdk';
const client = new PushFloClient({ publishKey: 'YOUR_PUBLISH_KEY' });
client.connect();
client.subscribe('my-channel', {
onMessage: (data) => {
console.log('Received:', data);
},
});
2. Server-side (Vercel API Route):
await fetch('https://api.pushflo.dev/api/v1/publish', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PUSHFLO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
channel: 'my-channel',
data: { hello: 'world' }
}),
});
3. Server-side (Cloudflare Worker):
export default {
async fetch(request, env) {
await fetch('https://api.pushflo.dev/api/v1/publish', {
method: 'POST',
headers: {
'Authorization': `Bearer ${env.PUSHFLO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
channel: 'my-channel',
data: { hello: 'world' }
}),
});
return new Response('Published!');
}
}
Conclusion
WebSockets and serverless are architecturally incompatible — but that doesn't mean your users have to suffer with slow, polling-based experiences.
The modern approach is to separate concerns:
- Your serverless functions handle business logic
- A managed service handles real-time connections
This gives you the best of both worlds: the simplicity of serverless AND the instant updates of WebSockets.
Ready to add real-time to your serverless app? Start with PushFlo's free tier — 500K messages/month, no credit card required.
Real-time without managing WebSocket servers
PushFlo holds the connections so your serverless functions don’t have to.
Related Articles
Do You Really Need Millions of Messages? The Real-time Pricing Trap
Why most real-time services sell you quotas you'll never use. A honest look at what indie developers actually need from WebSocket infrastructure.
How to Add Real-time Features to Next.js Without Managing Servers
Learn how to add WebSocket-powered real-time features like live notifications, chat, and dashboards to your Next.js app deployed on Vercel — without running your own WebSocket server.
Polling vs WebSockets: Cost Comparison for Serverless Apps
A detailed breakdown of the true costs of polling vs WebSocket-based real-time features in serverless applications. Includes calculations for Vercel, AWS Lambda, and managed WebSocket services.
