glimfly
web updated

What is a WebSocket, and why did realtime break on deploy?

In one sentence:A WebSocket is a single connection between a browser and a server that both sides keep open and can send messages over at any time, instead of the browser having to ask for updates one request at a time.

What it actually is

A WebSocket is a single connection between a browser and a server that stays open, so either side can send a message the instant it has one instead of one asking and the other answering.

HTTP is like sending a letter for every question: you write one, mail it, wait, get a letter back, and if you have another question you write and mail a new letter. A WebSocket is a phone call. You dial once, and after that either person can talk whenever they have something to say, no hanging up and redialing. The connection starts as a normal HTTP request asking to switch protocols (an Upgrade header), the server agrees, and from there both sides just talk over the same open line, addressed with ws:// for plain connections or wss:// for encrypted ones. Compare that to a typical API endpoint, which answers once per request and closes the connection when it’s done.

Keep the analogy honest: a WebSocket still needs something on the server actively holding the call open. A server built to answer one request and shut down can’t keep a phone call going, and that’s the detail that trips up vibe coders after deploy.

Why your AI just did this

Your AI reaches for a WebSocket, or a service built on one, any time you ask for something that updates itself: chat that appears without a refresh, a shared cursor moving in real time, a “someone is typing” indicator, a live count of who’s online. A plain fetch call can’t do that. It answers once and stops.

For most vibe-coding stacks, the agent wires up a managed service that runs the WebSocket server for you, rather than writing raw WebSocket code. If your project uses Supabase, the agent likely reaches for Supabase Realtime: you open a channel, subscribe to it, and either listen for database changes or send your own events, while Supabase keeps the actual connection alive on its own servers. Pusher and Ably work the same way. A Lovable or Bolt “live chat” feature is usually one of these managed services under the hood, not a WebSocket server your agent stood up from scratch.

When you’ll run into it

The classic sighting: the feature updates instantly while you test on localhost, then goes quiet the moment you deploy. In your browser console (F12 → Console) you’ll see something like:

WebSocket connection to 'wss://your-app.vercel.app/api/ws' failed:

One cause is the URL scheme. If your page loads over HTTPS, the browser refuses a plain ws:// connection outright as mixed content and only allows wss://. Local dev over http://localhost doesn’t trigger that block, which is exactly why it worked before you deployed.

The bigger cause is the host itself. A typical serverless function handles one request and shuts down; it was never built to hold a connection open for minutes. As of July 2026, Vercel Functions can serve WebSockets natively, but it’s a public beta, it needs Fluid compute turned on, and a plain Next.js API route needs an extra experimental_upgradeWebSocket() call to use it, it isn’t automatic. Even then, the connection still closes once the function hits its maximum duration, so the client has to detect the drop and reconnect. That’s why most teams don’t hand-roll this part: they point the realtime feature at a managed service built to hold many long-lived connections at once, rather than fighting their host’s function limits.

What to check

  • Open the Network tab, filter by “WS,” and confirm a connection actually opens (status 101 Switching Protocols) rather than never connecting at all
  • Check the URL scheme: wss:// in production, ws:// only works on localhost
  • If you’re using Supabase Realtime, confirm you called .subscribe() on the channel. Nothing arrives until you do
  • Ask what’s actually holding the connection open in production: a managed service, or your own function. If it’s your own function, ask your agent to name the host’s connection duration limit
  • Add reconnect logic on the client. Even a correctly configured WebSocket drops on deploys, network blips, and timeouts, and the UI should recover quietly instead of going stale

Say it like a dev

Instead of: “the chat doesn’t update live anymore after I deployed” Say: “The WebSocket connection fails after deploy, the console shows [paste the exact error]. What’s holding this connection open in production, and does it support long-lived connections?”

Instead of: “make the messages appear instantly” Say: “Add realtime updates with Supabase Realtime, subscribed to this table’s changes, instead of polling for new rows.”

Instead of: “it works on my machine but not live” Say: “This worked over ws://localhost, but the deployed site is HTTPS. Switch the client to wss:// and confirm the server side actually accepts the upgraded connection.”

People actually ask

“How does the chat update on the screen without me refreshing the page?”

It's using a WebSocket, a connection between your browser and the server that stays open instead of closing after one exchange. The server can push a new message down that same connection the moment it arrives, so your screen updates without you asking for anything. Most vibe-coding chat features get this from a managed service like Supabase Realtime rather than a WebSocket your agent wrote from scratch.

“Why did my realtime feature work locally but stop working after I deployed?”

Two common causes. Your production site is HTTPS, and browsers block a ws:// connection from an HTTPS page as mixed content, only wss:// is allowed. The other is your host: many serverless functions handle one request and shut down, they weren't built to hold a connection open for minutes, so realtime features usually run through a managed service like Supabase Realtime, Pusher, or Ably instead.

“Is a WebSocket the same thing as an API?”

It starts as one. Opening a WebSocket begins with a normal HTTP request asking the server to switch protocols. After that handshake, both sides keep the same connection open and can send messages over it at any time. A regular API endpoint answers once per request and closes; a WebSocket stays open so either side can speak first.

Related terms

Checked against

free tool · no signup

Just met this in a real session? Glim reads it in plain words.

Paste what your agent just did, a git diff, your terminal, or its summary, and Glim tells you what changed and what to check before you ship. Nothing stored.

Explain my session →