How to embed video on a website: self-host vs YouTube vs streaming host

Paste the YouTube iframe and you ship 540 KB of someone else's JavaScript. Self-host and you own the egress bill. Here is how to choose, with the arithmetic.

On this page
A round brass ship's porthole set into a plain wall
The window is easy to cut; what matters is whose wall the bytes arrive through.

Three places the bytes can come from#

Every video on a web page is one of three arrangements. To a visitor they can look identical; to your page, your bill, and your users' privacy they are nothing alike, because what separates them is where the bytes originate and who owns the player that draws them.

Self-hosting means you serve the encoded file from your own origin or CDN and point a <video> element at it. The browser fetches the file and plays it with native controls. Because no one format plays in every browser, you list a couple of fallbacks and, in MDN's words, "the browser will then use the first one it understands"1; which codec belongs in each source is a separate call. Everything downstream of that tag is yours: the encoding, the poster, the caption track, and the bandwidth.

A hosted embed is the pasted YouTube or Vimeo <iframe>. The file, the player, the CDN, and the encoding all belong to someone else. It costs nothing and it reaches everywhere, and, as the next two sections measure, it is heavy on the page and chatty with third parties.

A streaming host — Mux, Cloudflare Stream, or Bunny Stream — sits between them. You upload once; they encode into an adaptive ladder and deliver over their own CDN the way YouTube does, except the player is clean, the domain is yours, and nobody staples related-video thumbnails or ads to your demo. You pay by the minute watched or the gigabyte delivered.

The rest of this is the choice among the three, on the axes that genuinely differ.

The decision on one screen#

Axis Self-hosted <video> YouTube / Vimeo embed Streaming host (Mux, CF, Bunny)
Player control Full: native chrome or your own JS Almost none: their chrome, ads, related videos Full: their player, your branding
Adaptive bitrate No, unless you build the HLS ladder Yes, automatic Yes, automatic
Bytes on page load Your poster only ~540 KB player per embed A small player script
Delivery cost Your CDN egress $0 Metered: per minute or per GB
Third-party tracking None; first-party Cookies and view personalization None; first-party
Encoding and captions You do it They do it They do it
Fits READMEs, docs, one small clip Reach on YouTube's own surface A library watched at scale, on your domain

Read the table as four questions in order. Does the clip need to play smoothly at scale on unpredictable networks? A bare self-hosted MP4 can't, so that narrows things. Can you accept Google's player and its tracking sitting on your page? If not, the raw embed is out. Is the number you can't afford the delivery bill or the page weight? That decides between a streaming host and self-hosting. The sections below are those rows argued out.

What a pasted YouTube embed loads before anyone presses play#

The cheapest option is not free; it just bills you in kilobytes instead of dollars. Chrome's Lighthouse audit for third-party facades puts a figure on it: for a YouTube embed, "the facade weighs 3 KB and the player weighing 540 KB is loaded on interaction" when you defer it, and on first paint when you don't3. Half a megabyte of someone else's JavaScript, executed before anything is clicked, for a video most visitors will never play.

The fix is the facade pattern: render a lightweight placeholder and swap in the real iframe only when the visitor interacts. Lighthouse spells out the sequence — "On load: Add facade to the page. On mouseover: The facade preconnects to third-party resources. On click: The facade replaces itself with the third-party product"3. Paul Irish's drop-in custom element for exactly this reports rendering "approximately 224× faster" than the native embed4.

The catch, in the audit's own words, is that a facade lacks "the full range of functionality of the actual embeds": no autoplay, no live badges, a click's worth of latency before playback starts. For a landing hero that is a fair trade, and the LCP and poster mechanics that decide whether any embed wrecks first paint are worked out in detail elsewhere in this cluster. The point here is narrower: the raw iframe is the single most expensive way to put a video on a page, and almost nobody who pastes it knows the price.

Self-hosting's real bill, in dollars per thousand views#

Self-hosting trades page weight for an egress bill, and the bill is smaller than folklore claims, if you serve it from the right place. Work an example: one 2-minute 1080p demo at a lean 2 Mbps is about 30 MB per full view. Ten thousand full views a month is roughly 290 GB delivered, or 20,000 minutes watched. Here is that month on each option, at list rates verified in 2026.

Host Billed by List rate 10,000 views/mo
Self-hosted, commodity CDN GB delivered ~$0.005-0.01/GB8 ~$1.50-3, plus your own encoding
YouTube / Vimeo embed nothing $0 $0 (paid in ~540 KB/load and tracking)
Cloudflare Stream minutes delivered $1 / 1,000 min, bandwidth included5 ~$20
Mux minutes delivered $0.001/min at 1080p7 ~$20 at list
Bunny Stream GB delivered from $0.005-0.01/GB8 ~$1.50-3

The striking cell is not any single price; it is that the per-minute hosts (Cloudflare Stream, Mux) cost several times the per-GB hosts (Bunny, a commodity CDN) for the same footage. The reason is the billing unit, not the brand. At 2 Mbps, one minute of 1080p is about 15 MB, so a per-GB host at $0.01/GB charges roughly $0.00015 a minute against Cloudflare's $0.001, about seven times less. Per-minute billing only pulls even once bitrate crosses about 13 Mbps, which is 4K territory rather than a product demo. Bill by the gigabyte for ordinary 1080p; bill by the minute if you ship high-bitrate 4K, where a per-minute rate stops punishing you for pixels.

The folklore gets one thing wrong in the other direction, too: a hyperscaler CDN can charge several times a commodity CDN's rate for the identical bytes, so "self-hosting is cheap" holds only if you don't serve it from a big cloud's default egress. And video earns its keep only when the page still loads fast to begin with, a claim traced through the actual conversion data rather than assumed.

Adaptive bitrate is the line a single file can't cross for free#

The quiet reason a raw <video> loses at scale is that it hands every viewer the same file. A phone on a congested cell network downloads the identical 2 Mbps 1080p stream as a workstation on fiber, so the phone buffers and the desktop is under-served. Streaming hosts and YouTube solve this the same way: they encode your upload into a ladder of renditions and switch between them per segment as the connection changes. Cloudflare Stream, for one, "automatically encodes and delivers videos using the H.264 codec with adaptive bitrate streaming, supporting resolutions from 360p to 1080p" for "smooth playback across different devices and network conditions"6.

You can do this yourself: run ffmpeg to produce three or four renditions, generate an HLS playlist, and serve the segments. It works, and it is precisely the labor a streaming host charges you to skip. So the honest read is that self-hosting a single MP4 is fine for a short clip on a docs page or a README, where every viewer is on a decent connection and the file is small enough that one bitrate suits everyone. It stops being fine the moment the video is long, watched globally, or the difference between a smooth play and a spinner decides whether the demo lands. If the embed also has to autoplay muted the way a background loop does, the attribute set is the same whichever host serves the bytes.

The third party you embedded also embedded itself#

A YouTube iframe is not just a video; it is a Google-owned script running on your origin with its own agenda. By default the standard embed can use the view to personalize the visitor's experience across the web. YouTube's own privacy-enhanced mode, which you enable by switching the domain to youtube-nocookie.com, narrows that: a view in that mode "will not be used to personalize" the viewer's browsing, and any ads served "will likewise be non-personalized"9. It is an improvement, not an exit. The embed still loads Google's player and connects to Google's servers, and you have to opt in by editing the URL.

Self-hosting and streaming hosts sidestep the question, because the bytes and the player come from your own domain. Bunny Stream, Mux, and Cloudflare Stream serve a first-party player with no advertising cookies riding along on the play. For a privacy-sensitive product, a consent-conscious jurisdiction, or simply a page you would rather not hand to another company's analytics, that is the deciding column, and it is one self-hosting a <video> wins outright, since the only bytes are the ones you shipped. Owning the player also means you own the caption track instead of inheriting YouTube's auto-captions, and captions are not optional on muted playback.

Picking by the job, not the brand#

Match the mechanism to the task. A short clip in a README or docs page: self-host a single MP4, set preload="none" so nothing downloads until a click, which web.dev calls "the best chance of avoiding loading the video"2, and skip every host. A landing hero or a demo watched at scale on mixed networks: a streaming host, priced per GB for ordinary 1080p and per minute only if you serve 4K, so you get adaptive delivery and a clean first-party player without building an HLS pipeline. Reach and discovery on YouTube's own surface, where the algorithm and search are the whole point: use YouTube, but embed it behind a facade so the 540 KB player never loads on first paint. This is one row in the larger job of turning one recording into every placement.

Whichever row you land on, you still have to produce the file, and the file has to stay honest as the UI it shows keeps shipping. aidemo, the open-source engine we build, renders the demo from an agent-authored storyboard, so the embedded copy is a re-render rather than a reshoot when the product changes; it is browser-only, offers no GUI timeline editor, and expects an agent, not a person at a scrubber, to write the spec, which suits a demo pipeline and not a hand-tuned one-off.

Sources#

  1. MDN — The Video Embed element (<video>)
  2. web.dev — Lazy-loading video
  3. Chrome / Lighthouse — Lazy load third-party resources with facades
  4. paulirish — lite-youtube-embed
  5. Cloudflare Stream — Pricing
  6. Cloudflare Stream — Overview (adaptive bitrate)
  7. Mux — Pricing
  8. Bunny — Stream pricing
  9. YouTube Help — Embed videos and playlists (privacy-enhanced mode)

FAQ#

Is it better to self-host a video or embed it from YouTube?#

Neither wins outright; it depends on what you can't afford. YouTube is free and adaptive but loads a roughly 540 KB player per page view and attaches Google's tracking, so it fits reach on YouTube's own surface and content you don't mind branding. Self-hosting a <video> keeps the page light and first-party but hands every viewer one bitrate and puts the egress bill on you. If you want adaptive delivery and a clean player without either downside, a streaming host is the middle path, metered per GB (about $0.005-0.01) or per minute ($1 per 1,000).

Does embedding a YouTube video slow down my website?#

Yes, if you paste the stock iframe. Chrome's Lighthouse measures the YouTube player at about 540 KB loaded on the page3, executed on first paint whether or not anyone watches. The fix is a facade, a roughly 3 KB placeholder that swaps in the real player on click, which the lite-youtube-embed element reports rendering about 224 times faster.

How much does it cost to stream a demo video from your own site?#

For a 2-minute 1080p clip at about 2 Mbps (roughly 30 MB a view) watched 10,000 times a month, which is around 290 GB or 20,000 minutes, a commodity CDN or Bunny Stream runs about $1.50-3 at $0.005-0.01/GB, while Cloudflare Stream or Mux land near $20 because they meter by the minute. Per-GB billing is several times cheaper for ordinary 1080p; per-minute billing only pays off above about 13 Mbps.

This article was researched and drafted with AI assistance and reviewed by the aidemo maintainers. Tool capabilities and prices change — check vendor docs before deciding. How we research and correct our articles →