Adding product video to a WooCommerce page is usually a good call, right up until the page gets slow. The normal response is a caching plugin, image optimisation and perhaps a lighter theme. That work is real, and on a page of text and images it holds up.
None of it reaches the video. A page cache stores the HTML your site generates, and an MP4 is not HTML. The browser fetches that file separately, in ranged requests, straight from your origin. Nothing in an image pipeline touches a video file either, so the largest object on the page is the one object nobody optimised.
What makes the page slow is the file and who serves it. This covers what a self-hosted MP4 does to your server and to Largest Contentful Paint, then four fixes ordered by effort, each tied to the measurable it moves.
TL;DR
A self-hosted MP4 is one file at one bitrate, so a phone on a train downloads the same bytes as a desktop on fibre. Your origin holds every one of those connections open for the length of the download, and they compete with page requests. Above the fold, the video block is an LCP candidate through its poster.
Four fixes, cheapest first: lazy load the player below the fold, move delivery off the origin, serve adaptive renditions, defer the player bundle. One and four move LCP and INP. Two moves TTFB. Three fixes rebuffering, which Core Web Vitals never measures and your viewer always notices.
Why a self-hosted MP4 costs more than its file size
WordPress puts an uploaded MP4 in wp-content/uploads and hands the URL to a player. Three things follow from that, and none of them are visible in the file size.
Every visitor downloads the same bytes. One file means one bitrate. A 1080p master encoded for a desktop review goes to the phone on a train in exactly the form it goes to the office on fibre. The phone cannot ask for less, because there is nothing smaller to ask for.
Your origin holds the connection open. A static file normally bypasses PHP, so nginx or Apache serves it and ties up a connection slot for the whole download. Put an access check in front of that file, which is what membership and protected-upload plugins do, and PHP is now in the path instead. Those pools are small. A handful of people watching a long video can leave nothing for the visitors queued behind them.
Above the fold, the video block is an LCP candidate. Chrome measures Largest Contentful Paint against the largest element painted in the viewport, and a video element qualifies through its poster image. An autoplaying video with no poster can qualify through its first painted frame. The asset you actually control in both cases is the poster.
Which Core Web Vitals video actually moves
Core Web Vitals is three metrics, not four. Interaction to Next Paint replaced First Input Delay in March 2024. Time to First Byte and Total Blocking Time are useful diagnostics, and they are not themselves Core Web Vitals.
| Measure | Good threshold | What video does to it |
|---|---|---|
| LCP (Core Web Vital) | 2.5s or less | The poster is often the largest element in the viewport, and the video download competes with it |
| INP (Core Web Vital) | 200ms or less | Player JavaScript on the main thread delays the response to a tap |
| CLS (Core Web Vital) | 0.1 or less | A player that sizes itself after load pushes the content below it down |
| TTFB (diagnostic) | 800ms or less | The origin is busy serving video bytes when the page request arrives |
| TBT (lab diagnostic) | Lab signal for INP | Parsing and executing the player bundle blocks the main thread |
Google assesses each metric at the 75th percentile of real visits over a rolling 28-day window. A change you ship today appears in a lab tool at once and in your field data weeks later, so build the feedback loop around lab readings.
Fix 1: load the player only when it scrolls into view
Cheapest change, and on a long product or course page it is also the largest. A player that has not scrolled into view still costs bandwidth, connections and main-thread time at load, and the LCP element is competing for all three. The mechanism is worked through in guide to lazy loading HTML videos for your website, so the part worth adding here is where it backfires.
It backfires above the fold. Lazy loading the hero video delays the element Chrome is measuring, which pushes LCP out rather than pulling it in. Load the top player eagerly with a small, well compressed poster, and lazy load everything underneath it.
Doing this by hand means an IntersectionObserver per embed, a poster you generate and size yourself, and a fallback for the browsers that ignore the observer. On the FastPix block and shortcode, lazyload is on by default, and poster or thumbnailtime picks the still frame that LCP will end up measuring.
Fix 2: take delivery off your origin
Your server has one job during a page load, which is answering page requests quickly. Video bytes compete for the same workers, the same bandwidth and the same connection slots. Move delivery to a network built for it and the page stops queueing behind the video. You see that as a lower TTFB on the page, not only on the video.
Be honest about the limit of this one. Moving the file does not change the file, and a large MP4 on a CDN is still one bitrate for everybody. Moving a WordPress Media Library off your server works through what offloading fixes and what it leaves behind.
With the FastPix WordPress plugin, visitors stream from the FastPix network instead of your host. Your origin leaves the delivery path, so a slow query on your server never reaches somebody watching a video.
Fix 3: serve renditions instead of one file
Adaptive delivery replaces the single file with a ladder of renditions and a manifest. The player picks a rung that fits the connection and moves as the connection changes. ABR vs MBR covers the difference between a real ladder and a set of fixed quality buttons.
Core Web Vitals will not reward you for this. LCP, INP and CLS measure the page, not the playback, so a page can pass all three while every mobile viewer watches a spinner. The measure that catches it is rebuffering, and seeing it needs playback data. Video analytics in WordPress covers why a retention cliff is usually delivery.
Renditions are the part nobody wants to run themselves, because it means an encoding pipeline, storage per rung and a manifest per video. Uploading through the FastPix plugin produces the ladder during processing, with a top resolution you choose per batch and 1080p as the default.
Fix 4: defer the player bundle
A player is JavaScript, and JavaScript on the main thread is what INP charges you for. Parse and execute time lands in Total Blocking Time in lab tools, then reaches the field as a tap that takes too long to do anything. Understanding video player size and performance covers the weight side.
Galleries are where the cost multiplies. A page that builds one player per thumbnail pays the bundle cost per item, and twelve of those will stall a mid-range phone. Posters plus a single deferred player instance is the shape that survives mobile, which is the approach in how to build a product video gallery in WordPress.
A hosted player usually arrives from a third-party domain, which adds a DNS lookup, a TLS handshake and a request before any code runs. The FastPix player ships inside the plugin, so nothing loads from a remote server, and aspectratio reserves the box before the player draws, which keeps that layout shift out of CLS.
The order to run them in, and what to measure
Effort and payoff do not line up neatly, so the order below is cheapest first rather than biggest first.
- Lazy load every player below the fold. Minutes. Moves LCP and TBT.
- Compress the poster on the above-the-fold player and keep that one eager. Minutes. Moves LCP.
- Move delivery off your origin. An afternoon. Moves TTFB, and frees the workers the rest of the page needs.
- Defer the player bundle and run one instance per page. An afternoon. Moves TBT and INP.
- Switch to adaptive renditions. A migration. Fixes rebuffering, which no Core Web Vital reports.
Take a lab reading on one real page before you touch anything. Record LCP, the element Chrome names, TBT and CLS, then repeat after each change. Field data confirms the result later. The lab reading is what tells you whether the last change helped.
Replace one above-the-fold embed and run PageSpeed Insights again
Pick the page with the worst LCP and note which element Chrome names. Then swap that self-hosted embed for a FastPix embed.
Three of the four fixes land in that one change, because lazy loading is already on, delivery leaves your origin and the renditions come out of the upload. Run the page again and compare the same four numbers. The free plan covers 10 videos with no card, which is more than one page needs.
Frequently Asked Questions (FAQs)
Does video slow down WordPress Core Web Vitals?
A self-hosted video usually does. It is the largest object on the page, your origin serves it, and the connection stays open for the whole download, so page requests queue behind it. Above the fold it is also an LCP candidate through its poster. A page cache never sees the file and an image optimiser never touches it.
Is a video the LCP element on a WordPress page?
It can be. Chrome treats a video element as an LCP candidate through its poster image, and an autoplaying video with no poster can qualify through its first painted frame. The lever in both cases is the poster, because that is the asset you choose, compress and size.
Does lazy loading video improve Core Web Vitals?
Below the fold, yes. A player that has not scrolled into view still costs bandwidth, connections and main-thread time at load, and the LCP element competes for all three. Above the fold it does the opposite, because it delays the element Chrome is measuring. Guide to lazy loading HTML videos covers the mechanism.
Why is my WordPress site slow only when a video is on the page?
The video competes with the page for the same server resources. A static file ties up a connection slot on nginx or Apache for the whole download. If an access check sits in front of the file, PHP is in the path and a worker is tied up instead. Those pools are small on shared hosting.
Does moving video to a CDN fix Core Web Vitals?
It fixes the origin contention and lowers Time to First Byte, which is a real gain. It does not change the file. A large MP4 on a CDN is still one bitrate, so a viewer on a slow connection downloads what a viewer on fibre downloads. Moving a WordPress Media Library off your server covers that split.
Do Core Web Vitals measure video buffering?
No. LCP, INP and CLS measure the page: what paints, how fast it responds, whether the layout moves. None look at startup time, stalls or rendition switching. A page can pass all three while mobile viewers watch a spinner, which is why playback data comes from somewhere else.
What is a good LCP for a page with video?
The same target as any other page. Google's good threshold for Largest Contentful Paint is 2.5 seconds or less, assessed at the 75th percentile of real visits. A page with video is not judged more gently, so the poster has to paint inside that budget.
Is INP still part of Core Web Vitals?
Yes. Interaction to Next Paint replaced First Input Delay in March 2024, and the current set is LCP, INP and CLS. Time to First Byte and Total Blocking Time remain useful for finding the cause of a bad LCP or INP, and neither is a Core Web Vital.










