Video on your website without slowing it down

How to add video without hurting speed: where to host it, how to encode it small, how to load it late with a poster or facade, and what to check.

IntermediateHow-to9 min readUpdated October 30, 2026

In this guide
  1. What video costs
  2. Do you need the video?
  3. Where to host it
  4. Make a self-hosted video small
  5. Mark it up
  6. Load it late
  7. Serve it well
  8. A background video, with care
  9. How to check
  10. Mistakes to avoid
  11. Check your own page
  12. Sources

What video costs

The 2025 Web Almanac found that video files are the largest of all file types on the pages it measured, from 7 KB at the 10th percentile to about a gigabyte at the top. Among home pages that loaded video, the median page loaded 288 KB of it on desktop and 512 KB on mobile. At the 90th percentile the figures were about 5 MB on desktop and 6.1 MB on mobile. The Almanac warns about its own figures: browsers often fetched the same video more than once with different range headers, which inflated the byte counts and seemed worse on mobile. Read the numbers as a sense of scale.

Bytes are only part of the cost:

Do you need the video?

What you wantA lighter choice
A short silent loop, like a GIFA muted, looping video file. See replacing animated GIFs with video
Mood behind a headingA still image, or a short loop that starts after the page has painted
To explain a product or serviceA video the visitor chooses to play, behind a poster
A long talk, lesson or webinarA video platform or a streaming service, not your own server

A picture costs far fewer bytes than a clip. Keep video for the jobs where movement or sound really helps.

Where to host it

OptionWhat you getWhat you take on
Your own MP4 or WebM fileNo third-party code, and full control of the player and of what loadsThe bandwidth, and the job of encoding the file well. Best for short clips
A video platform's embedThe platform stores the video, makes several qualities and runs the playerAn iframe with its own scripts and requests. Put a facade in front of it
A streaming serviceAdaptive streamingA player and a service to choose

MDN describes adaptive streaming as quality that changes in real time with the visitor's bandwidth. It names HLS and MPEG-DASH as the main formats, and notes that cloud services will convert your media to both. For a long video that many people watch, that is a better fit than one big file.

Make a self-hosted video small

The main levers on the size are the frame size, the quality setting, the frame rate and the length. MDN's codec guide says that shrinking the frame before encoding can make a video much smaller. Its example is a 1080p video reduced to 720p, which can look better than the original encoded at full size with the quality cut to reach a size target. It adds that more motion and more noise mean larger files, and that 24 frames per second and above looks smooth.

These two commands were tested with FFmpeg 6.1. They resize to 1280 pixels wide, set 24 frames per second and drop the sound:

# MP4 (H.264) with the index at the front of the file
ffmpeg -i source.mov -vf "scale=1280:-2,fps=24" -c:v libx264 -crf 28 -pix_fmt yuv420p -an -movflags +faststart intro.mp4

# WebM (VP9)
ffmpeg -i source.mov -vf "scale=1280:-2,fps=24" -c:v libvpx-vp9 -crf 38 -b:v 0 -an intro.webm

For a video with sound, replace -an with -c:a aac in the MP4 command and with -c:a libopus in the WebM command. MDN's recommendations pair MP4 and H.264 with AAC audio, and WebM with Opus.

What the options do:

To check the order in an MP4, run ffprobe -v trace intro.mp4 2>&1 | grep -m2 -E "type:'(moov|mdat)'". The first line should name moov.

Offer both files. MDN's first suggestion for typical websites is WebM with AV1, with Safari on older Apple devices as the exception. It calls VP9 widely supported and says MP4 with H.264 works in every major browser. The browser takes the first source it can play, so list the WebM first and the MP4 last.

Format is not the main lever. In the Almanac's data, the median WebM file on desktop home pages was larger than the median QuickTime or MPEG file. What sits inside the file, its size, length and quality, matters more than the wrapper.

Mark it up

<video controls preload="none" width="1280" height="720"
       poster="/img/intro-poster.jpg">
  <source src="/video/intro.webm" type="video/webm; codecs=vp9">
  <source src="/video/intro.mp4" type="video/mp4">
  <track kind="captions" src="/video/intro.en.vtt" srclang="en" label="English">
  <p>Your browser cannot play this video. <a href="/video/intro.mp4">Download it</a>.</p>
</video>

MDN's delivery guide notes that some mobile browsers may ignore autoplay and preload. Make the poster work on its own.

WordPress's Video block has a preload setting that defaults to metadata, per its block definition. WordPress core adds the loading attribute to the img and iframe tags it prints, but its helper for that supports only those two tags, so set preload for video yourself.

Load it late

An embedded player. Do not put the player on the page until someone asks for it. A facade is a static picture with a play button. Lighthouse's documentation describes the pattern in three steps: add the facade on load, preconnect when the pointer moves over it, and swap in the real player on a click. Its 2020 example showed a facade weighing 3 KB in place of a player weighing 540 KB. Measure your own embed.

Shopify's Dawn theme does this in its video section. The poster is a button. The YouTube or Vimeo iframe, or Shopify's own video, sits inside a template element and is added to the page when the button is pressed. The template element's content is not rendered by default, MDN says.

<div class="video-facade" style="aspect-ratio: 16 / 9">
  <button type="button" aria-label="Play video: How we speed up a website">
    <img src="/img/intro-poster.jpg" width="1280" height="720" alt="" loading="lazy">
  </button>
  <template>
    <iframe src="https://player.example.com/embed/VIDEO_ID?autoplay=1"
            title="How we speed up a website" allow="autoplay; encrypted-media"
            allowfullscreen></iframe>
  </template>
</div>

<script>
  document.querySelectorAll('.video-facade').forEach((box) => {
    box.querySelector('button').addEventListener('click', () => {
      const frame = box.querySelector('template').content.firstElementChild.cloneNode(true);
      box.replaceChildren(frame);
      frame.focus();
    });
  });
</script>

I ran this page in headless Chromium. Before the click only the page and the poster were requested. After it, the player address was requested and the frame took focus. The button is a real button with a label, so a keyboard works. The fixed shape stops the page from jumping. The third-party scripts guide shows a version that builds the iframe in script.

An iframe that must be on the page. MDN says loading="lazy" on an iframe defers loading until it nears the viewport, and only when JavaScript is enabled. The Almanac, quoting web.dev, puts the saving at around 500 KB for a YouTube embed, and found no iframe loading setting on 91.5% of desktop pages.

A video file. Use the poster and preload="none". For a muted loop that should start when it scrolls into view, use the observer script in the GIF guide. See also lazy loading.

Not for the first screen. A video at the top of the page should load at once. It may be your LCP element, so keep its poster small.

Serve it well

A background video, with care

A looping video behind a heading is the heaviest way to decorate a page. The Almanac lists background videos among the things that can quickly balloon page size. If you keep one:

This script adds the video only on wide screens for people who have not asked for reduced motion. I ran it in headless Chromium: at 1200 pixels wide the WebM was requested and played, and at 400 pixels, or with reduced motion on, only the poster was requested.

<video muted loop playsinline preload="none" width="1280" height="720"
       poster="/img/hero-poster.jpg" data-hero></video>

<script>
  const hero = document.querySelector('video[data-hero]');
  const wide = matchMedia('(min-width: 900px)').matches;
  const calm = matchMedia('(prefers-reduced-motion: reduce)').matches;
  if (wide && !calm) {
    hero.innerHTML = '<source src="/video/hero.webm" type="video/webm; codecs=vp9">' +
                     '<source src="/video/hero.mp4" type="video/mp4">';
    hero.load();
    hero.play().catch(() => {});
  }
</script>

How to check

  1. Count the bytes before play. Load the page with the Network panel open and filter by Media. Only the poster should load until a visitor asks for the video, unless it is a first-screen clip. See the Network panel.
  2. Find the LCP element. In PageSpeed Insights or Lighthouse, check whether the video or its poster is the element. See how to fix a slow LCP.
  3. Look for layout shift when the video or embed appears.
  4. Check the headers of the video request: Content-Type, Accept-Ranges and Cache-Control.
  5. Throttle the network and watch what the page does on a slow connection. Then try a real phone. See testing on real phones.
  6. Look at the third-party rows. An embed can add several requests to other hosts.

Mistakes to avoid

Check your own page

Run the free audit and look at the largest files and the LCP element. Then open the Network panel, filter by Media, reload, and list every video file with its size. For each one, ask whether a visitor needs it before pressing play.

Sources

Start here

Free website audit

Live Google data for any public page: mobile and desktop.

Mobile + desktop · Lighthouse lab test · Real-user Chrome data when Google has it

Rather have this done for you? Speed optimization · Book a call

Related guides

All guides →