Fix Video Not Playing in Browser — Codec, MIME, and CORS Errors
Your <video> tag shows a black box, a play button that does nothing, or an error icon. HTML5 video playback fails silently — the browser gives almost no useful error info. Here's how to debug it systematically.
Common Symptoms
- Black video area with no playback
- Play button does nothing when clicked
- "This video format is not supported" (Firefox)
- "The media could not be loaded" (Chrome)
- Audio plays but no video (or vice versa)
- Video works locally but not in production
Debug Step 1: Check the Codec
The most common cause. Open the browser console:
const video = document.querySelector('video');
video.addEventListener('error', (e) => {
console.log('Error code:', video.error.code);
console.log('Message:', video.error.message);
});
Error code 4 = MEDIA_ERR_SRC_NOT_SUPPORTED → codec issue.
| Codec | Chrome | Firefox | Safari | Edge | |-------|--------|---------|--------|------| | H.264 (MP4) | ✅ | ✅ | ✅ | ✅ | | H.265 (MP4) | ⚠️ HW only | ❌ | ✅ | ⚠️ HW only | | VP9 (WebM) | ✅ | ✅ | ✅ 14.1+ | ✅ | | AV1 (WebM/MP4) | ✅ 70+ | ✅ 67+ | ✅ 17+ | ✅ 79+ |
Fix: Transcode to H.264:
ffmpeg -i input.mkv -c:v libx264 -crf 23 -c:a aac output.mp4
Or serve multiple formats:
<video controls>
<source src="video.webm" type="video/webm">
<source src="video.mp4" type="video/mp4">
</video>
Test with our codec samples: H.264 MP4 · H.265 MP4 · VP9 WebM · MKV
Debug Step 2: Check MIME Type
curl -I https://your-site.com/video.mp4
# Must return: Content-Type: video/mp4
# NOT: Content-Type: application/octet-stream
Fix (Nginx):
types {
video/mp4 mp4 m4v;
video/webm webm;
}
Debug Step 3: Check CORS Headers
If video is on a CDN/different domain:
Access to video at 'https://cdn.example.com/video.mp4'
from origin 'https://your-site.com' has been blocked by CORS policy
Fix: Add CORS headers on the CDN/server serving the video:
location /videos/ {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS";
}
Debug Step 4: Check Range Requests (206 Partial Content)
Video seeking requires the server to support HTTP Range requests. Without it, seeking breaks and large videos may not play.
# Test Range request support
curl -I -H "Range: bytes=0-1024" https://your-site.com/video.mp4
# Must return: HTTP/2 206 Partial Content
# With: Content-Range: bytes 0-1024/12345678
Most static file servers (Nginx, Apache, S3, CDNs) support Range by default. If yours doesn't, seeking will break.
Debug Step 5: Autoplay Policies
Modern browsers block autoplay with audio. If your video doesn't start:
<!-- WON'T work — blocked by browser -->
<video autoplay src="video.mp4"></video>
<!-- WORKS — muted autoplay is allowed -->
<video autoplay muted playsinline src="video.mp4"></video>
Test with: sample-silent.mp4 (no audio track — always autoplays)
Test with Sample Videos
| Test | File | What to Verify | |------|------|---------------| | H.264 baseline | sample-10mb.mp4 | Universal playback | | H.265 codec | sample-h265.mp4 | Safari/HW decode | | VP9 WebM | sample-10mb.webm | Chrome/Firefox | | Silent autoplay | sample-silent.mp4 | Autoplay policy | | Portrait (vertical) | sample-portrait.mp4 | Orientation | | 4K resolution | sample-4k.mp4 | Decode performance | | 60fps | sample-60fps.mp4 | Frame rate |
See our Video Codecs Cheat Sheet for the complete codec compatibility reference.