Compress video for the web: bitrate, codecs, and one-call presets

Most people compress a web video by dragging the bitrate slider down until the file is small enough, then wonder why it looks like a JPEG from 2006. The file size is fine. The playback is worse than it needs to be. And half the time the video still won't start playing until it fully downloads.
Quick answer: To compress video for the web, encode with H.264 (libx264) using CRF quality instead of a fixed bitrate, pick a preset likemedium, downscale to your target resolution, and add-movflags +faststartso the file streams before it finishes downloading. One command does it locally; a video compression API does the same job in one call with no FFmpeg to install.
Conventional wisdom says smaller video means lower bitrate. That's partly right. Bitrate does control size. But for the web, the thing that makes a video small and watchable isn't a lower bitrate, it's letting the encoder spend bits where the eye actually notices. That's what CRF encoding does, and it's why a well-compressed 1080p clip can look sharp at a third the size of a bitrate-capped one.
Bitrate vs CRF: why quality-based compression wins for web
CRF (Constant Rate Factor) tells the encoder a quality target and lets the bitrate float scene by scene. Fixed bitrate wastes bits on easy frames and starves complex ones. For web delivery, CRF is the better default.
With libx264, CRF runs 0 to 51. Lower is higher quality and bigger. A few anchors that hold up in practice:
- CRF 18: visually lossless, large files. Use for archival, not web.
- CRF 23: the x264 default. Good quality, reasonable size.
- CRF 26–28: the web sweet spot. Noticeably smaller, still clean on a phone or laptop.
The -preset flag is a separate lever: it trades encode time for compression efficiency, not quality. veryslow squeezes out a smaller file at the same CRF than ultrafast, but takes far longer. medium is the sane default; slow is worth it for content you encode once and serve many times.
Pick a codec: H.264, H.265, VP9, or AV1
Choose the codec by where the video plays, not by which one compresses best on paper. Here's the honest state of things:
- H.264 (libx264). Plays everywhere: every browser, every phone, every embed. Largest files of the modern codecs, zero compatibility risk. This is the correct default for a general web audience.
- H.265 / HEVC (libx265). Roughly 25–50% smaller than H.264 at the same quality, but browser support is patchy and it carries licensing baggage. Good for controlled apps, risky for open web.
- VP9. Royalty-free, strong compression, well supported in Chrome and Firefox. Common on YouTube.
- AV1. The best compression of the four and royalty-free, but slow to encode and still spotty on older devices.
If you're serving a landing page or a public embed, encode H.264 and move on. If you control the player and the audience, H.265 or AV1 buys real bandwidth savings.
The FFmpeg command to compress video for the web
Here's the command that does it right, with faststart so the video plays during download instead of after:
ffmpeg -i input.mp4 \
-c:v libx264 -crf 26 -preset slow \
-vf "scale=-2:1080" \
-c:a aac -b:a 128k \
-movflags +faststart \
output.mp4
What each part earns you:
-crf 26 -preset slow: quality-based compression at the web sweet spot.-vf "scale=-2:1080": downscale to 1080p height;-2keeps the aspect ratio and forces an even width (H.264 requires even dimensions).-c:a aac -b:a 128k: standard web audio; 128 kbps AAC is transparent for most content.-movflags +faststart: moves the MP4moovatom to the front so browsers can start playback immediately. Skip this and your video buffers to 100% before the first frame shows.
Not sure what you're feeding in? Run ffprobe first to check the source resolution, codec, and bitrate so you're not upscaling or re-encoding something that's already fine.
One API call instead: a video compression API
The FFmpeg command works great until you need it inside a product, an n8n flow, or an agent, and you hit the part nobody warns you about: installing and running FFmpeg. Binaries, versions, a server that stays up, and long jobs you have to babysit.
A video compression API is the same operation with none of the operations. You submit a job with your compression settings, poll or get a webhook, and download the output. No FFmpeg to install, no servers to run.
curl -X POST https://api.ffmpeg-micro.com/jobs \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "https://example.com/input.mp4",
"operation": "compress",
"settings": { "codec": "h264", "crf": 26, "preset": "slow", "height": 1080 }
}'
The request shape and exact parameters live in the docs; the point is that one call replaces a whole media microservice. Here's the tradeoff laid out plainly:
| FFmpeg CLI on your own box | FFmpeg Micro API | |
|---|---|---|
| Setup | Install binary, manage versions | None, just an API key |
| Scaling | You run and scale the server | Managed, no servers to run |
| Long jobs | You babysit the process | Poll or webhook when done |
| Fits a workflow | Shell out and hope | Works with n8n, Make, and Zapier |
Bitrate targets and presets by resolution
If your delivery needs a fixed bitrate (adaptive streaming ladders, hard bandwidth caps), skip CRF and target a rate. Rough H.264 starting points for web:
| Resolution | Target video bitrate | Audio |
|---|---|---|
| 1080p | 4,000–6,000 kbps | 128 kbps AAC |
| 720p | 2,000–4,000 kbps | 128 kbps AAC |
| 480p | 800–1,500 kbps | 96 kbps AAC |
For a true target bitrate, use two-pass encoding so the encoder plans the whole file:
ffmpeg -y -i input.mp4 -c:v libx264 -b:v 4000k -pass 1 -an -f null /dev/null && \
ffmpeg -i input.mp4 -c:v libx264 -b:v 4000k -pass 2 \
-c:a aac -b:a 128k -movflags +faststart output.mp4
Two passes take roughly twice as long but hit your size budget far more accurately than one pass. For most web pages, CRF is simpler and looks better at a given size. Reach for two-pass only when the byte budget is non-negotiable.
Common pitfalls when compressing web video
These are the ones that actually bite people:
- Forgetting
+faststart. The most common web-video mistake. Without it the browser downloads the entire file before playing. Always add it for progressive-download MP4. - Odd dimensions.
libx264rejects odd width or height. Usescale=-2:1080instead ofscale=-1:1080to force an even value. - Cranking bitrate instead of CRF. A high fixed bitrate on a simple video wastes bytes; a low one on a busy scene smears. CRF avoids both.
- Upscaling. Compressing a 720p source to 1080p adds size and zero detail. Check the source with ffprobe first.
- Wrong codec for the audience. Shipping H.265 to the open web means some visitors see a black frame. Default to H.264 unless you control the player.
When NOT to use a video compression API
Be honest about the fit. If you're compressing one video by hand on your laptop, the FFmpeg CLI is free and takes 30 seconds, so just run the command above. If you need frame-accurate creative editing with a timeline and effects, that's an editor, not an API. And if every millisecond of latency matters for a real-time stream, encode at the edge rather than round-tripping to a job API.
The API earns its keep when compression is a step in a pipeline, running dozens or thousands of times, inside code or a no-code workflow, where installing and scaling FFmpeg is the real cost. Same logic applies whether you're shrinking clips for email under Gmail's 25 MB limit or squeezing uploads under the WhatsApp 16 MB cap.
FAQ
What's the best FFmpeg command to compress video for the web?
Encode H.264 with -c:v libx264 -crf 26 -preset slow, downscale with -vf "scale=-2:1080", use -c:a aac -b:a 128k for audio, and add -movflags +faststart so it streams during download. CRF 26 hits the web quality-vs-size sweet spot.
What bitrate should I use for web video?
For quality-based encoding, use CRF and let bitrate float. If you need a fixed target, aim for about 4,000–6,000 kbps for 1080p, 2,000–4,000 kbps for 720p, and 800–1,500 kbps for 480p, all with 128 kbps AAC audio.
H.264 or H.265 for web video?
Use H.264 for anything public: it plays in every browser and on every device. H.265 compresses 25–50% smaller but has patchy browser support and licensing costs, so reserve it for apps where you control the player and audience.
Why does my compressed video still buffer before playing?
You almost certainly forgot -movflags +faststart. Without it, the MP4's metadata (moov atom) sits at the end of the file, so the browser must download the whole thing before playback starts. Re-encode with faststart to fix it.
Can I compress video without installing FFmpeg?
Yes. A video compression API like FFmpeg Micro runs the same FFmpeg operations behind a REST call, so you submit a job and download the output with no binaries, versions, or servers to manage. It also works from n8n, Make, and Zapier.
Ready to skip the install and the babysitting? Sign up free and compress your first video with one API call.
About Javid Jamae
Founder & CEO at FFmpeg Micro
Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.
You might also like

Concatenate clips programmatically: a composition API primer
Concatenate clips programmatically with a composition API: why FFmpeg's concat demuxer breaks on mixed inputs, the real CLI commands, and the one-call fix.

How to Add AI Voiceover to Video with ElevenLabs and FFmpeg Micro
Generate AI voiceover with ElevenLabs TTS and merge it into video using FFmpeg Micro API. Replace or mix audio with no FFmpeg binary needed.

How to Use FFmpeg with Next.js (No Binary Required)
Use FFmpeg Micro cloud API to process video in Next.js Route Handlers and Server Actions. No binary installation, no serverless timeouts.
Ready to process videos at scale?
Start using FFmpeg Micro's simple API today. No infrastructure required.
Get Started Free