This template is designed for low-cost ergonomic websites. The objective is instant playback with bounded worst-case egress and minimal infrastructure. This is how I've optimised https://gaurv.me/🎰 to serve Pink Floyd's music (no strikes).
I won't discuss blocking bots or CAPTCHA verification. You can get decent coverage by hosting on Cloudflare for free, though I'm not claiming it works 100% of the time as advertised. But it's better than nothing. Once you've figured this out, lets see how we can serve heavy videos in most cache-efficient ways and budget contraints. Workflow has been written using AI with parts of this post edited by me.
encode assets
full video
Use a single MP4 file optimized for progressive playback. Do not segment.
ffmpeg -i input.mp4 \
-c:v libx264 -crf 22 -preset slow \
-c:a aac -b:a 128k \
-movflags +faststart \
full.mp4
While most browsers (albiet with some nuances) support range requests, it's safer to move video metadata to the beginning. faststart is the conventional way to guarantee this.
preview video
Modern browsers do not request entire video at once but in ranges so playback can start immediately. By creating a preview we only serve the full video if and only if the viewer wants.
ffmpeg -i input.mp4 \
-ss 0 -t 15 \
-vf scale=640:-2 \
-c:v libx264 -profile:v baseline -level 3.0 \
-b:v 600k -g 30 \
-c:a aac -b:a 96k \
-movflags +faststart \
preview.mp4
Note: If you are comfortable with the fact that any page view that reaches play can result in the full video being streamed, then you do not need a preview.
upload to S3
Use a single bucket for all video assets. No bucket policy changes are required. Create separate folders and upload the assets.
/preview/preview.mp4
/full/full.mp4
Why different folders? To set object-level metadata.
Preview object:
Cache-Control: public, max-age=31536000, immutable
Content-Type: video/mp4
Full object:
Cache-Control: public, max-age=86400
Content-Type: video/mp4
CloudFront configuration
Use a single CloudFront distribution.
Origin configuration:
-
S3 origin
-
Origin Access Control enabled
Behaviors:
-
/preview/*→ Managed-CachingOptimized -
/full/*→ Managed-CachingOptimized -
Allowed methods: GET, HEAD
-
Range requests enabled
-
Compression disabled
Do not use HLS or DASH. A single MP4 provides better cache efficiency and lower request volume.
video attributes
Never reference the full video during initial render. The video element must be created without a source.
<video id="video" playsinline preload="metadata" poster="/poster.webp" controls></video>
<button id="play-full">Play Full</button>
intent-gated loading
Attach the preview only when playback starts. Attach the full video only after explicit user action.
const v = document.getElementById("video");
v.addEventListener("play", () => {
if (!v.src) {
v.src = PREVIEW_URL;
v.load();
}
});
playFullBtn.onclick = () => {
v.src = FULL_URL;
v.load();
v.play();
};
Rules:
-
No
<source>tag -
No
preload="auto" -
No full video URL in initial HTML
cost control invariants
The following invariants must always hold.
Page load must trigger zero video bytes. The preview must cap accidental egress. The full video must load only on explicit user action. Use a single MP4 file without segmentation. Apply long cache TTLs to previews and short TTLs to full assets.
Bottom Line
Playback speed comes from faststart and CDN caching. Cost control comes from intent gating. These concerns are separate and must not be conflated.
Verify behavior in browser DevTools:
On page load, no MP4 requests should appear. On first play, only the preview file should be requested. On full playback, the full MP4 should begin streaming. Responses should be 206 Partial Content with x-cache: Hit from CloudFront.