Serving video from your own server is different from serving pages. The files are large, players request them in an unusual way, and a single popular video can consume more bandwidth in a day than the rest of the site does in a month.
Byte-range requests are how video works
A player does not download the file and then play it. It asks for ranges; the first few seconds, then more as it goes, and a different range entirely when someone drags the progress bar.
That requires the server to support range requests, which a normal static file server does by default.
What breaks it is serving video through your application: a PHP script reading the file and sending it usually ignores range requests, so seeking does not work and the whole file is transferred even when the visitor watches ten seconds.
Serve video as a static file wherever access control allows it. Where it does not, make the script honour ranges rather than streaming from the start. How to Sell Digital and Downloadable Products walks through the protected case.
Put the moov atom at the front
An MP4 contains an index the player needs before it can start. If that index is at the end of the file, the player must download the whole thing before playback begins.
The symptom is a video that takes an age to start on a slow connection and plays instantly on a fast one, which reads as a bandwidth problem in place of a file layout problem.
ffmpeg -i input.mp4 -c copy -movflags +faststart output.mp4
It is a stream copy, so it takes seconds and costs no quality. Do it to every file you serve.
Encode for delivery, not for archiving
The source file is not the file to serve.
H.264 in MP4 with yuv420p plays everywhere. Match the resolution to how it is displayed, serving 4K to a player 800 pixels wide costs bandwidth for detail nobody sees.
Two or three renditions covers most cases: something small for phones on mobile data, something standard, and one larger if the content warrants it. For the encoding settings, see FFmpeg Performance Optimization Tips.
Adaptive streaming, when it is worth it
HLS or DASH splits the video into segments at several qualities, and the player switches as the connection changes. That is what stops buffering on a poor connection.
The cost is more files, more storage and a more complex pipeline. For a handful of short videos, a single well-encoded MP4 is simpler and works.
For anything long, or an audience on variable connections, adaptive is the difference between watchable and abandoned. How to Set Up HLS Streaming with FFmpeg explains producing it.
Bandwidth is the real constraint
Video consumes transfer at a rate nothing else on a website approaches. A ten-minute video at a modest bitrate is tens of megabytes per view.
Work out the arithmetic before you launch: file size times expected views, against your monthly allowance. Overage is billed per terabyte and the rate is often much higher than the plan implies. Understanding Bandwidth, Port Speed and Network Limits explains reading the numbers.
Port speed matters too: enough concurrent viewers will saturate a 1 Gbps port regardless of how much monthly transfer you have.
A CDN pays for itself here
For video specifically, a CDN is not a refinement. It serves from near the viewer, absorbs the concurrency, and removes most of the transfer from your own allowance.
Set long cache lifetimes on the video files, since they never change once encoded, and use a versioned filename if you ever replace one, rather than purging. How to Choose and Set Up a CDN walks through the setup.
Cache headers and hotlinking
Set a long expiry on video files. A returning viewer then does not re-download what they already have.
And check whether other sites are embedding your files directly, which bills your bandwidth for their visitors. On video that is expensive enough to be worth acting on, unlike most hotlinking. How to Enable Hotlink Protection deals with what it also blocks.
Posters and preload
Give every video a poster image, so the page shows something immediately instead of a black rectangle.
Then set preload="metadata" in place of the default on a page with several videos. Without it, browsers may begin buffering every video on the page, which costs you bandwidth for videos nobody plays and makes the page slow for everyone.
Know when to stop self-hosting
Self-hosting gives you control, no advertising, and no third party watching your viewers.
A video platform gives you adaptive streaming, global delivery and bandwidth that is not your problem, and it is genuinely the right answer when video is a large share of your traffic instead of an occasional page element.
The honest test is the arithmetic above. If the bandwidth cost of your video exceeds the cost of everything else you run, self-hosting is a decision to keep making in place of a default.
How much bandwidth there is to save in the first place is decided at encoding time. How to Choose a Video Codec: H.264, H.265, VP9 or AV1 deals with picking one, including serving two so no visitor is left without a playable file.
Seek previews served as individual files are hundreds of requests per video. How to Generate Preview Sprites for a Video Player explains doing it in one.
Confirm range requests actually work
Seeking depends on the server honouring partial requests, and several common configurations quietly disable it.
curl -sI https://example.com/video.mp4 | grep -iE 'accept-ranges|content-length|content-type'
curl -s -o /dev/null -w '%{http_code}\n' -r 0-1023 https://example.com/video.mp4
The second request must answer with a partial content status. Anything else means the whole file is being sent for every seek, which is felt as a player that stalls whenever a viewer moves the position.
Compression is the usual culprit, since a server compressing the response cannot serve ranges from it. Video is already compressed, so excluding those types costs nothing and restores seeking.
Watch what the delivery actually costs
Video changes the shape of a hosting bill, and the figure that matters is not the file size.
awk '$7 ~ /\.(mp4|webm|m4s|ts)$/ {s+=$10} END {printf "%.1f GB\n", s/1073741824}' ~/logs/example.com
awk '$7 ~ /\.(mp4|webm)$/ {print $7}' ~/logs/example.com | sort | uniq -c | sort -rn | head -5
ss -tn state established | wc -l
One popular file can account for most of a month's transfer on its own. The connection count matters too, since each viewer holds one for the length of the video rather than for the length of a page load.
That is the constraint people meet first: a server comfortable with a thousand page views an hour struggles with fifty simultaneous video streams, and no amount of page optimisation changes it.