After the browser transfers a file, the server may still scan, transcode, extract metadata, or generate previews. The interface needs status updates, but the most fashionable real-time transport is not automatically the most reliable choice for an event guest on a mobile browser.
Start with the update contract
Define states and transitions before choosing transport:
accepted -> queued -> processing -> ready
accepted -> rejected
processing -> processing_error
Every status response should include a monotonically increasing version or timestamp. The client can ignore events older than the state it already knows.
Polling is a strong baseline
HTTP polling works through proxies, resumes naturally after a page wake, and is easy to cache and rate-limit. Use adaptive intervals: one second just after acceptance, then back off to several seconds when processing takes longer.
const delay = Math.min(8000, 1000 * 2 ** slowChecks);
Add jitter when many guests finish at the same time. Stop when the state is terminal, the page is gone, or a server-provided retry time says to wait longer.
SSE fits one-way progress
Server-Sent Events are attractive when the server only pushes status. The browser reconnects with a last-event ID, and the wire format is simple. But mobile networks and some intermediaries silently kill idle connections.
Send occasional heartbeats and treat reconnection as normal. The reconnect handler must fetch current state because events may have been missed beyond the server replay window.
Do not keep one SSE stream per file. Subscribe by guest session or album and filter authorized upload IDs on the server.
WebSockets add more responsibility
WebSockets make sense when the client also sends frequent commands over the same channel or when a host moderation console needs many rapid updates. For a guest waiting on two files, they add connection authentication, heartbeat, reconnect, replay, and load-balancer complexity without much user benefit.
Never rely on the socket as the only source of truth. A reconnect must reconcile through an HTTP status endpoint.
Design transport fallback
The client can prefer SSE and fall back to polling after repeated disconnects. Keep state semantics identical across transports. A UI component should consume normalized status events, not know whether they came from HTTP or a stream.
statusStore.apply({uploadId, version, state, progress});
This also makes automated tests easier.
Respect background behavior
Browsers throttle timers and may suspend connections when the screen locks. Pause aggressive polling when hidden, but perform an immediate status fetch on visibility change. Do not display a failure merely because no event arrived while suspended.
Protect authorization
A status channel can leak filenames, processing details, or other guests' uploads. Authenticate every subscription, scope it to allowed IDs, and avoid putting access tokens in URLs that appear in logs. Recheck authorization during long-lived connections.
Measure the right outcome
Compare time from server state change to visible UI update, reconnect frequency, requests per completed upload, and battery/network cost. A persistent connection with constant failures is not more “real time” than two-second polling.
I help build Gathmo and have a commercial interest in upload UX. The Gathmo browser workflow is one example where processing status matters; the transport tradeoffs are platform-neutral.
For most guest upload pages, reliable polling is the reference implementation. Add SSE when it measurably improves latency or load, and choose WebSockets only when bidirectional behavior justifies the operational surface.
Top comments (0)