Back to blog

Developer Guides

How WebRTC File Transfer Works (A Practical Guide for Developers)

Learn how WebRTC file transfer works: data channels, STUN/TURN, NAT traversal, and when a relay beats peer-to-peer.

August 1, 20268 min read
How WebRTC File Transfer Works (A Practical Guide for Developers)

When two browsers send a file to each other, it can look like magic. You drop a file, the other tab starts receiving bytes, and nothing obvious like a cloud folder appears in the middle. Under the hood, that path is usually WebRTC—a set of browser APIs originally built for real-time audio and video, later used for arbitrary data.

WebRTC file transfer is not one protocol with a single happy path. It is a negotiation: discover public addresses, punch through NAT if possible, fall back to a relay if not, then open an encrypted data channel. This guide walks through that pipeline in practical terms, including when peer-to-peer fails and when a room-style upload is the better engineering choice.

What WebRTC actually moves

WebRTC is a stack, not a file-sharing product. For files, the important pieces are:

  • ICE (Interactive Connectivity Establishment) to gather candidate network paths
  • STUN servers to learn your public IP and port mapping
  • TURN servers to relay traffic when direct paths fail
  • DTLS for encrypting the data channel
  • SCTP data channels to send bytes that are not audio or video

A video call uses media tracks. A file transfer uses a data channel. Same ICE dance, different payload. If you only remember one sentence, remember this: the file does not travel over your signaling WebSocket unless you put it there on purpose.

Signaling is not the file path

Before two peers can talk, they need a signaling channel—usually a WebSocket or HTTP API on a server you control. Signaling carries SDP offers, answers, and ICE candidates. It coordinates the session. It should not be mistaken for the transfer itself.

If signaling is down, WebRTC never starts. If signaling is up but ICE fails, the UI can still look "connected" until you actually test the data channel. That mismatch is why many first implementations look fine in a demo and fail the first time two people join from different mobile networks.

NAT, STUN, and why home networks fight you

Most devices sit behind NAT. The browser thinks it is `192.168.1.42:54321`. The public internet sees something else. For a peer to send packets back, that mapping must be discovered and kept alive.

A STUN server answers a simple question: what do I look like from the public internet? The client adds that reflexive address as an ICE candidate. If both sides sit behind cooperative consumer NAT, hole punching often works. Symmetric NAT, carrier-grade NAT on mobile, hotel double NAT, and locked-down office firewalls frequently break the direct path.

TURN is the expensive honest fallback

When every direct candidate fails, TURN relays the bytes through a server. The transfer can still use WebRTC APIs and still encrypt the channel, but you now pay for bandwidth, and the relay sees connection metadata such as timing and volume.

Practical rule of thumb:

  • Same LAN or same Wi-Fi: host candidates often win and feel instant.
  • Home to home on typical consumer NAT: STUN is often enough.
  • Corporate, campus, or mobile CGNAT: budget for TURN, or skip live P2P.

Do not promise users "direct, no server" if you have not provisioned TURN. Without a relay, a large share of real-world pairs never connect. That is not a bug in your chunking code. It is the internet.

Data channels: how the file actually moves

Once ICE selects a candidate pair and DTLS completes, you open an RTCDataChannel. A typical file protocol looks like this:

  1. Exchange metadata: name, size, MIME type, optional hash.
  2. Slice the file into chunks, often 16–64 KB.
  3. Send chunks with sequence numbers.
  4. Apply backpressure with `bufferedAmount` and `bufferedAmountLowThreshold`.
  5. Reassemble and verify on the receiver before you call the transfer done.

Ordered vs unordered, reliable vs partial

Default data channels are reliable and ordered, which is what most file tools want. Unordered or unreliable modes exist for games and lossy real-time data. They are the wrong default for an APK, a zip, or a crash dump.

Watch these gotchas:

  • `bufferedAmount` can explode if you `send()` as fast as the disk reads. Pause when the buffer is high.
  • Mobile browsers background tabs and freeze timers. Transfers stall when the phone sleeps.
  • Chunk size vs overhead: huge chunks delay the first progress tick; tiny chunks waste CPU.
  • Memory: reading a multi-gigabyte file into one `ArrayBuffer` can kill a tab. Stream with `Blob.slice` or readable streams.

When peer-to-peer fails (and you should expect it)

P2P is a best effort, not a guarantee. Common failure modes:

  • One peer is on a VPN that blocks UDP.
  • School or office firewalls allow only HTTPS on 443/TCP.
  • Both peers sit behind symmetric NAT.
  • The page is not a secure context, so the browser refuses WebRTC features.
  • One user closes the tab. There is no daemon holding the socket.
  • IPv6 and IPv4 mismatch with incomplete ICE candidates.

When P2P fails, you have two honest product choices:

  1. TURN relay — still WebRTC, still a live session, both users online at once.
  2. Room or store-and-forward upload — sender uploads to temporary storage; the receiver downloads later over ordinary HTTPS.

When a relay or room upload is better

Choose a non-P2P or hybrid path when:

  • Recipients will download hours later.
  • You need more than two people without building a full mesh.
  • Mobile users will lock their phones mid-transfer.
  • You cannot operate a reliable TURN fleet.
  • Compliance needs a server-side scan or an audit log.

Browser room tools such as [PeerPizza](/) take the temporary-room approach: no account, a room code, optional PIN, chat plus files, chunked uploads without a fixed per-file size cap, and automatic cleanup after roughly two hours of inactivity. That is a different tradeoff from a live WebRTC pipe. Keep a local backup either way—temporary rooms expire on purpose.

Other options include self-hosted object storage, signed cloud URLs, or a TURN-backed WebRTC product if both people can stay online. The right answer depends on whether "both tabs open right now" is a reasonable requirement.

Security caveats developers skip

WebRTC data channels are encrypted with DTLS. That is necessary and not sufficient.

  • Signaling is the trust boundary. If an attacker joins the room or intercepts the offer, they become a peer. Authenticate signaling. Use short-lived room tokens. Offer a PIN when the content is sensitive.
  • You still share a file with a person. Encryption to the wrong recipient is a successful attack, not a crypto failure.
  • TURN operators can see metadata even if payloads stay encrypted: who connected, how much, and when.
  • Do not treat STUN or TURN credentials as immortal secrets. Issue time-limited credentials.
  • Hash the file if integrity matters. Accidental truncation can look like success if you only watch `onclose`.
  • Short public room codes can be guessed. Rate-limit room creation. Log enough to handle abuse reports without keeping files forever.

WebRTC does not remove the need for ordinary access control. It only encrypts the path you already decided to open.

A practical architecture checklist

If you are building or evaluating a WebRTC file feature, require these before you call it production:

  • HTTPS (a secure context) on every page that opens a peer connection
  • Signaling with authentication or unguessable room IDs
  • STUN plus TURN with metering, not STUN alone
  • Chunked send with backpressure
  • Progress on both sides, including a clear failed state
  • Size or hash verification at the end
  • UX that explains the live-session rule: both people stay online, or they should use a room upload instead

A checklist will not make NAT disappear. It will stop you from shipping a demo that only works on the same Wi-Fi.

Troubleshooting WebRTC file transfers

Use this section when localhost works and production does not.

Connection never establishes

  • Check ICE gathering. Zero candidates besides host usually means the STUN URL is wrong, blocked, or never configured.
  • Confirm both peers receive remote candidates over signaling. A one-way signaling bug looks exactly like a NAT problem.
  • Ask one side to disable VPN and try again.
  • Test with a known-good TURN server. If TURN works and host or server-reflexive candidates fail, NAT is the story.

Channel opens, then the transfer stalls

  • Log `bufferedAmount`. If it only grows, you are not respecting backpressure.
  • Confirm the receiver is handling `onmessage` and not blocked on UI work.
  • Watch for tab freeze on mobile Safari when the screen locks.
  • Avoid huge `send()` payloads that hit browser message-size limits.

File arrives corrupted or truncated

  • Compare byte length before you celebrate.
  • Compute a SHA-256 of the blob on both sides for important artifacts.
  • Do not drop the last chunk when `readyState` flips to closing.
  • Avoid treating `bufferedAmount === 0` as "the other side has the file." That only means your local send buffer drained.

Fast at the office, slow at home

  • Same-network host candidates can hit the LAN. Remote paths go through the slower of two uplinks.
  • Home upload bandwidth is usually the bottleneck, not the data channel API.
  • If TURN is selected, you also pay the relay's path and capacity.

WebRTC vs store-and-forward at a glance

ConcernLive WebRTC data channelTemporary room or cloud upload
Both users onlineRequiredNot required
Typical NAT painHigh without TURNLow (ordinary HTTPS)
Extra server bandwidthOnly if TURN is usedAlways (upload plus download)
Best forSame session, same timeAsync handoff
Tab crash or phone lockOften fatalUpload can continue independently
Long-term archiveNoOnly if you keep the object

Neither model is "more secure" by slogan. Security is access control, expiry, and whether the right human received the file.

Final thoughts

WebRTC file transfer is a live, encrypted pipe assembled from ICE, STUN, optional TURN, and SCTP data channels. It is excellent when two browsers are online together and the network path is kind. It is a poor default when you cannot run TURN, when recipients show up later, or when mobile tabs will sleep. Know the failure modes, put a relay or a temporary room behind the send button, and never confuse "the packet was encrypted" with "the right person got the only copy." Keep the original file on disk. Protocols expire. Backups do not have to.

Try PeerPizza: free temporary rooms to chat and share files — no signup, room codes, optional PIN, auto cleanup. Open home