Video formats in WordPress: MOV, MP4 or WebM (Adobe effects & web)

Video formats in WordPress: MOV, MP4 or WebM (Adobe effects & web)

Last verified: October 12, 2026
20 min read
News
UI/UX designer
500+ WP projects

Common problem for motion designers and video editors starting with websites: “I made a beautiful animation in Adobe After Effects, rendered to .mov (in best quality!), uploaded to WordPress, and nothing. Black screen, error, or the file weighs 500 MB and loads for an hour.”

The file is not broken. It was made for an edit suite, and a browser is not an edit suite.

#Why MOV files won’t play in WordPress

The .mov format (the QuickTime container) is standard in creative work, especially on Macs. .mov is a container, not a codec. What makes it unusable on the web is usually what is inside it: ProRes, or uncompressed frames.

The sizes are not a matter of opinion. Apple publishes them. At 1920x1080 and 29.97 fps, the ProRes white paper gives target data rates of 45 Mbps for 422 Proxy, 102 Mbps for 422 LT, 147 Mbps for 422, 220 Mbps for 422 HQ, 330 Mbps for 4444 and 495 Mbps for 4444 XQ. The appendix in the same document restates those as gigabytes per hour: 422 HQ is 99 GB per hour, which is about 1.65 GB for a single minute of footage. A twenty second hero loop straight out of After Effects can therefore be larger than the entire rest of the site.

Two things then go wrong inside WordPress, in this order.

The upload may not finish at all. wp_max_upload_size() reads upload_max_filesize and post_max_size from PHP and returns the smaller of the two. On shared hosting those are frequently left at modest defaults, and a multi hundred megabyte POST dies before the media library ever sees it.

If it does finish, it still will not play. WordPress accepts the file because wp_get_mime_types() contains 'mov|qt' => 'video/quicktime'. But wp_get_video_extensions(), the function that decides what core considers a playable video source, returns only mp4, m4v, webm, ogv and flv. MOV is not on that list. So the attachment sits in the library looking like a video and behaving like a download.

Forcing the markup by hand does not save you either. MDN’s container guide lists QuickTime support as “only older versions of Safari, plus other browsers that supported Apple’s QuickTime plugin”, marks every codec in its QuickTime table as unsupported in Firefox, and notes that the QuickTime framework is no longer available at all starting in macOS 10.15 Catalina. ProRes does not appear among the codecs MDN’s video codec guide documents for browsers.

#Why ProRes is not for the web

ProRes was designed for professional editing, where the priority is keeping maximum quality across rounds of grading and recompositing. Every frame is stored with light, intra-frame compression, which makes files huge but easy to scrub and cut in post.

The web has different priorities. Visitors expect video to start in seconds. Mobile connections have limited bandwidth. Browsers need codecs built for efficient decoding, and MDN says as much in its codec guide: no lossless or near lossless codec is generally available in browsers, because uncompressed 1080p with 4:2:0 subsampling already needs at least 1.5 Gbps.

#Does exporting at maximum quality improve web video

Many designers assume that better quality means a better experience. A master file that takes minutes to arrive delivers a worse experience than a compressed copy that starts playing at once and that a casual viewer cannot tell apart from the master on a laptop screen.

#What video format does WordPress support

Two formats cover the whole web, and the choice between them is less dramatic than it was when this article was first written.

#1. MP4 with H.264 (AVC)

The safe default. caniuse puts H.264 in MP4 at 97.26 percent global support (data checked on 12 October 2026), going back to Chrome 4 and Safari 3.2. If you ship one file, ship this one.

Two settings decide whether your export actually works, and both are invisible in a preview player.

Chroma subsampling. ProRes 422 is, as the name says, 4:2:2, and the 422 family is 10 bit. Hand that to libx264 without instruction and it encodes a High 4:2:2 profile stream that browsers will not decode. We checked this with a ProRes 422 HQ test file: without a pixel format flag, ffprobe reported the output as High 4:2:2 with yuv422p10le. Force 8 bit 4:2:0 with -pix_fmt yuv420p.

Where the index lives. FFmpeg’s muxer documentation describes -movflags +faststart as an option to “run a second pass moving the index (moov atom) to the beginning of the file”, and notes it is not enabled by default. Without it the browser cannot begin playback until the whole file has arrived, which is exactly the “loads for an hour” symptom.

If you export from Adobe Media Encoder rather than the command line, pick H.264 as the format, not QuickTime, and then check both settings on the result. The format list is the only thing that matters; preset names move between releases.

For predictable results when exporting for WordPress:

  • Resolution: 1920x1080 for content video, 1280x720 for secondary clips.
  • Quality: CRF between 23 and 28 in libx264, instead of chasing a fixed bitrate.
  • Frame rate: keep the source rate (usually 24, 25 or 30 fps).
  • Audio: AAC at 128 to 192 kbps stereo, or no audio track at all if the clip is decorative.
  • Pixel format: yuv420p, verified with ffprobe on the exported file.
  • Profile and level: pin them with -profile:v high -level:v 4.0 for 1080p, so the file matches the codecs string you declare in the markup (more on that below).

#2. WebM with VP9

An open format developed by Google. Google’s VP9 page says the codec “can reduce video bit rates by as much as 50% compared with other known codecs”. Treat that as a ceiling reached on hard content, not a number you will hit on every clip, but the saving on a flat motion graphic is real.

The stale advice on this topic is that WebM is a Chrome and Firefox format. It has not been one for years. caniuse records full WebM support in Safari on macOS from version 16 and in Safari on iOS from 17.4, with Safari 14.1 to 15.6 already playing it on macOS 11.3 Big Sur or later. That puts full support at 96.05 percent globally, a little over a point behind H.264 in MP4.

#AV1 codec browser support

AV1 sits one generation further on and compresses better than VP9, but its rollout lags. caniuse gives it 80.39 percent full support, rising to 95.02 percent only if you count partial support. Safari is the partial case: on macOS and iOS it decodes AV1 only on devices with a hardware decoder, such as the iPhone 15 Pro or an M3 MacBook Pro.

MDN’s video codec guide now recommends WebM with AV1 and Opus as the first choice for a typical website, and MP4 with AVC and AAC as the broadly compatible second. That makes AV1 a first <source>, never the only one. AV1 encoding is also much slower than H.264 or VP9, so it pays off on content that is encoded once and played many times.

#HTML5 video tag with WebM and MP4 sources

If you have to self host, for instance as a hero background, list sources best first. The browser takes the first one it can decode and never looks at the rest.

<video autoplay loop muted playsinline preload="none"
       poster="/media/hero-poster.avif"
       width="1920" height="1080">
  <source src="/media/hero.webm" type='video/webm; codecs="vp9, opus"'>
  <source src="/media/hero.mp4" type='video/mp4; codecs="avc1.640028, mp4a.40.2"'>
  Your browser doesn't support video.
</video>

The attributes are doing specific jobs:

  • muted plus playsinline: mobile browsers refuse autoplay without both. Without playsinline, iOS takes the video fullscreen the moment it starts, which destroys a background loop.
  • preload="none": nothing is fetched until playback is requested, so the poster carries the first paint. Use metadata if you need duration up front, and auto almost never.
  • poster: an AVIF or WebP still is a fraction of the video and is what the visitor actually sees during the first seconds.
  • the codecs= part of the type string: it lets the browser reject a source without downloading a byte of it.
  • width and height: always set them so the box is reserved before the video loads, which prevents Cumulative Layout Shift, one of the Core Web Vitals.

#How to write the codecs string for MP4 and WebM

Many embed snippets declare avc1.42E01E for the MP4 source. That string declares Baseline profile (42) at level 3.0, while the FFmpeg command further down produces a High profile file. A codecs string that describes a different stream than the one you ship is a wrong claim to the browser.

MDN’s guide to the codecs parameter defines the AVC form as avc1.PPCCLL: two hex digits for the profile, two for the constraint flags, two for the level. High profile is 64 with constraints 00, and level 4.0 is decimal 40, hex 28. So High at level 4.0 is avc1.640028. AAC-LC audio in MP4 is mp4a.40.2. For WebM, MDN lists the short forms vp9, vp8, opus and vorbis.

AVC profileProfile byteConstraint byteTypical use
Constrained Baseline4240constrained resources, video calls
Baseline4200older form of the above
Main4D00standard definition broadcast
High6400HD broadcast and Blu-ray; also what our libx264 test encodes produced

Read the real values from the exported file instead of guessing them:

ffprobe -v error -select_streams v:0 \
  -show_entries stream=profile,level,pix_fmt -of csv=p=0 web.mp4

In our test, a 1080p encode with -preset slow and no level flag came back as High,50, level 5.0, because the slow preset uses more reference frames than level 4.0 allows at that resolution. With -profile:v high -level:v 4.0 it came back as High,40, which is what avc1.640028 declares.

#Video block attributes in the block editor

In the block editor you do not have to write the tag by hand. The core Video block’s block.json carries autoplay, controls, loop, muted, playsInline, poster, preload, src and tracks as attributes, with preload defaulting to metadata, and playsInline has its own toggle in the sidebar. The Cover block has no such attribute: its block.json declares no playsInline key, and its save output hardcodes autoPlay, muted, loop and playsInline on the background video, so a Cover video loop is always inline and there is no toggle to check. Neither block writes a codecs parameter into the markup.

#How the video shortcode picks sources

Classic content and many page builders still go through the [video] shortcode, which core renders with wp_video_shortcode(). Its source, in wp-includes/media.php, explains several things people report as bugs.

  • A MOV becomes a link. When src points to a file whose extension is not in wp_get_video_extensions(), the function returns a plain <a class="wp-embedded-video"> link to the file instead of a player.
  • Source order is fixed. You can pass one attribute per extension (mp4, m4v, webm, ogv, flv), and the function emits the <source> elements in that list order. [video webm="a.webm" mp4="a.mp4"] therefore puts the MP4 first, and every browser that decodes H.264 picks it.
  • The type carries no codecs. Each <source> gets a bare MIME type from wp_check_filetype(), such as video/mp4.
  • Defaults. preload is metadata, muted is false, and the player is 640x360 unless you pass width and height; a larger width is scaled down to the theme’s $content_width.
  • Player library. The output is wrapped for MediaElement.js by default; the wp_video_shortcode_library filter changes that.

Adding mov through the wp_video_extensions filter makes the shortcode render a player for it, but it does not give Chrome or Firefox a decoder, so the fix is still re-encoding.

#Server MIME types for MP4 and WebM

A file can be encoded perfectly and still be served with the wrong Content-Type. Check the response header in the browser’s Network tab. nginx’s default mime.types file already maps mp4 to video/mp4, webm to video/webm and mov to video/quicktime. On Apache, if the header comes back as text/plain or application/octet-stream, add the mapping in .htaccess:

AddType video/mp4 .mp4
AddType video/webm .webm

#Video file size optimization

File size matters for user experience and for SEO. A video that takes too long to load raises bounce and drags down Core Web Vitals.

There is no universal table of target sizes: the same second of footage weighs very differently in a static shot and in a grainy pan. Fix the quality with CRF, measure the resulting file, and decide on that number.

#Free compression tools

  1. HandBrake: free and open source. Its feature list names AV1, H.265, H.264, MPEG-4, MPEG-2, VP8 and VP9 as video encoders, with MP4, MKV and WebM as containers.
  2. FFmpeg: the command line tool behind most of the others, and the one used in every example here.
  3. Shutter Encoder: a graphical interface over FFmpeg, easier to start with.

#How to compress video with FFmpeg

# MOV to web MP4 (H.264, High profile, level 4.0)
ffmpeg -i input.mov -c:v libx264 -preset slow -crf 23 \
  -profile:v high -level:v 4.0 -pix_fmt yuv420p \
  -c:a aac -b:a 128k -movflags +faststart output.mp4

# MOV to WebM with VP9 and Opus
ffmpeg -i input.mov -c:v libvpx-vp9 -crf 33 -b:v 0 -row-mt 1 \
  -pix_fmt yuv420p -c:a libopus -b:a 96k output.webm

# 720p version for mobile
ffmpeg -i input.mov -vf scale=1280:720 -c:v libx264 -preset slow -crf 23 \
  -pix_fmt yuv420p -movflags +faststart output-mobile.mp4

The -crf value controls quality: lower values mean higher quality and a larger file. For the web, a CRF of 23 to 28 for H.264 or 30 to 35 for VP9 is a sensible range to start measuring from.

Verify the export rather than assuming, because a missing +faststart is silent:

grep -abo -m2 -e moov -e mdat output.mp4

That prints the byte offset of the first moov and the first mdat. You want the moov offset to be the smaller one. On our test file without the flag, mdat came first at byte 44 and moov sat at the end of the file; with the flag, moov was at byte 36.

#How to publish video with transparency (alpha channel)

VP9 in WebM carries an alpha channel if you encode to yuva420p. MDN’s codec guide lists VP9 in all current Chrome, Edge, Firefox, Opera and Safari versions, with the note that Safari does not support alpha transparency in it; caniuse carries the same note for WebM in Safari. Apple’s route is HEVC with alpha, which it documents in its HEVC Video with Alpha Interoperability Profile. If an After Effects composition needs a real alpha channel on the web, you are shipping two files and a solid colour fallback.

#Self-hosted video vs YouTube or Vimeo

Self hosting gives you one file at one bitrate delivered by progressive HTTP download. There is no adaptive ladder. WordPress builds resized renditions for images at upload time and does nothing equivalent for video, because core ships no transcoder. A visitor on a weak mobile connection gets your 1080p file or gets nothing, and stalls while it buffers.

So the rule is about what the video is, not how big it is.

#When to host video on your own server

  • Background video: short, silent animations behind a hero headline.
  • Private video: content that must not sit on a public platform.
  • Brand requirements: when the player has to be fully your own, without third party logos.
  • Load weight: for very short clips, a YouTube embed drags in a third party player and its cookies for something that is closer to a moving background image.

#When to use YouTube or Vimeo

  • Any video with sound longer than about 30 seconds that a visitor came to watch: an interview, a tutorial, a case study walkthrough.
  • Educational or marketing content that benefits from YouTube search.
  • Video you want shared on social networks.
  • Sites on limited bandwidth, where adaptive streaming matters.

Bandwidth is the other half of that decision. A 10 MB loop on a page that gets fifty thousand views a month is half a terabyte of egress, and on entry level hosting that is a conversation with your provider.

#How Google indexes self-hosted video

Google’s video SEO best practices add a third angle to the format question, and it does not line up with the browser one.

Google can process MOV, browsers cannot play it. Google’s list of supported video file types includes MOV, MP4, M4V, WebM, OGV and more. That makes a MOV indexable in principle and still useless to the visitor, so it is not an argument for shipping one.

Only a watch page gets the full set of video features. Google defines a watch page as one whose main purpose is to show a single video. A blog post that reviews an embedded video, or a product page with a 360 video, is explicitly not a watch page; such pages can still show up as text results and in Google Images with a video badge. A decorative hero loop will never be a video result, and it does not need to be.

The poster doubles as the thumbnail. If you use the <video> element, Google reads the poster attribute as the preferred thumbnail. Supported thumbnail formats are BMP, GIF, JPEG, PNG, WebP, SVG and AVIF, at least 60x30 pixels, at a stable URL that Googlebot is allowed to fetch.

Do not hide the video behind an interaction. Google asks that videos not depend on swiping, clicking or typing to load, and that a video injected by JavaScript appear in the rendered HTML shown by the URL Inspection tool. That matters for the lazy loading pattern below: fine for a decorative loop, risky for a video you want indexed.

Structured data. The VideoObject documentation lists three required properties: name, thumbnailUrl and uploadDate (ISO 8601, with a timezone). contentUrl should point at the video file itself and is preferred; embedUrl points at a player and is the alternative when the file URL is not available. For a self-hosted MP4, contentUrl is simply the file’s URL in wp-content/uploads.

#How to make video responsive in WordPress

WordPress does not adapt self-hosted video to screen sizes on its own, so that part you write yourself.

#CSS for responsive video

/* Responsive container for video */
.video-container {
    position: relative;
    width: 100%;
    padding-bottom: 56.25%; /* 16:9 ratio */
    height: 0;
    overflow: hidden;
}

.video-container video,
.video-container iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

#Serving different resolutions by device

<video autoplay loop muted playsinline>
  <!-- Mobile: 720p -->
  <source
    src="video-720p.webm"
    type="video/webm"
    media="(max-width: 768px)"
  >
  <!-- Desktop: 1080p -->
  <source src="video-1080p.webm" type="video/webm">
  <source src="video-1080p.mp4" type="video/mp4">
</video>

The media attribute on a video <source> is an old feature with a gap in the middle: according to MDN’s compatibility data, Firefox 53 to 119 honoured it only inside <picture>, and support for <audio> and <video> came back in Firefox 120.

#How to make video accessible in WordPress

Accessible video is required for the European public sector under the Web Accessibility Directive, and outside it, it simply makes the content understandable to more people. The relevant WCAG 2.2 success criteria are short and specific:

  • 1.2.2 Captions (Prerecorded), level A: captions for all prerecorded audio in synchronized media.
  • 1.2.5 Audio Description (Prerecorded), level AA: an audio description for prerecorded video content.
  • 1.4.2 Audio Control, level A: any audio that plays automatically for more than 3 seconds needs a way to pause or stop it, or a volume control independent of the system volume.
  • 2.2.2 Pause, Stop, Hide, level A: moving content that starts automatically, lasts more than five seconds and is shown alongside other content needs a way to pause, stop or hide it.

The last one is the one hero loops break. A muted background video with loop and no controls runs forever, so it needs a visible pause button, or it should stop after a few seconds.

#How to add captions with the track element

Captions on a self-hosted video are a WebVTT file referenced by a <track> element. caniuse puts WebVTT support at 97.26 percent, the same as the <video> element itself.

<video controls>
  <source src="video.mp4" type="video/mp4">
  <track kind="captions" src="captions.vtt" srclang="en" label="English" default>
  <track kind="descriptions" src="descriptions.vtt" srclang="en" label="Audio description">
</video>

A minimal WebVTT file looks like this:

WEBVTT

00:00:00.000 --> 00:00:04.000
Welcome to this video.

00:00:04.000 --> 00:00:08.000
[Soft music in the background]

The core Video block stores the same thing in its tracks attribute, so captions added in the editor end up as <track> elements in the saved markup.

#Minimum accessibility checklist for video

  1. Closed captions: for deaf and hard of hearing users, as a WebVTT file in a <track> element.
  2. Audio description: for blind users, describing what is only visible.
  3. Keyboard controls: the player must be usable without a mouse.
  4. Text alternative: the poster or thumbnail needs a meaningful description where it carries information.
  5. No autoplay with sound: video with sound should never start on its own.

#How to defer loading a self-hosted video

WordPress core’s loading="lazy" handling applies to images and iframes, not to the <video> element, which does not support that attribute. For a self-hosted video, what defers the download is preload="none" together with a poster. If you need more, load the source from JavaScript with an IntersectionObserver when the element enters the viewport, keeping in mind Google’s note above for any video you want indexed.

#How to convert MOV to MP4 for WordPress

  1. Edit and master in After Effects or Premiere against .mov and ProRes. That part was never wrong.
  2. Export for the web as H.264 in MP4, forcing -pix_fmt yuv420p, -movflags +faststart and a profile and level you then declare in codecs.
  3. Confirm the export: moov at a lower byte offset than mdat, and ffprobe reporting yuv420p and High,40, not yuv422p10le.
  4. Add a VP9 WebM copy and list it before the MP4 in the <video> element. Add AV1 above both if you are chasing the last few hundred kilobytes.
  5. Give the element a poster image and preload="none", and keep muted and playsinline on any autoplaying loop, plus a pause control if it runs longer than five seconds.
  6. Always set width and height to avoid layout shift.
  7. If the clip has sound and someone is meant to sit and watch it, put it on YouTube or Vimeo instead and embed it.

If this sits inside a build we maintain, we set the encode step up once in the deploy pipeline so nobody has to remember the flags. That is part of our WordPress development services.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Want this implemented on your site?

If you want to convert the article into a working site improvement, redesign, or build plan, I can define the scope and implement it.

Related cluster

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Frequently asked questions

Practical answers to apply the topic in real execution.

Why does my .mov upload but refuse to play in WordPress?#
The upload works because video/quicktime is in the default mime map returned by wp_get_mime_types(). Playback fails because wp_get_video_extensions() returns mp4, m4v, webm, ogv and flv, and mov is not on that list, so the Video block never hands the browser a source it can decode. MDN's container guide limits QuickTime support to older Safari versions and browsers with the retired QuickTime plugin.
How big is a ProRes file really?#
Apple's own white paper puts ProRes 422 HQ at 1920x1080 and 29.97 fps at about 220 Mbps, and its appendix converts that to 99 GB per hour, so roughly 1.65 GB per minute. Plain ProRes 422 is 147 Mbps and 66 GB per hour. That is before you reach the default PHP upload ceiling that wp_max_upload_size() reads from upload_max_filesize and post_max_size.
Is WebM still a Chrome and Firefox only format?#
No, and that advice is the most common stale claim on this topic. caniuse records full WebM support in Safari on macOS from version 16 and in Safari on iOS from 17.4, putting full global support at 96.05 percent, about a point behind H.264 in MP4. An MP4 fallback is still worth keeping for older iOS devices that will never get an update.
Why does my exported MP4 only start playing after it fully downloads?#
The moov atom is at the end of the file. FFmpeg's muxer documentation describes -movflags +faststart as a second pass that moves that index to the beginning, which is what lets a browser start playback from a partial download. Check the order by scanning the file for the two atom names, for example grep -abo -m2 -e moov -e mdat web.mp4, and confirm the moov offset is the smaller one.
Can I put a transparent After Effects animation on a web page?#
Partly. VP9 in WebM carries an alpha channel when you encode to yuva420p, and MDN names Safari as the one browser that does not render it. Apple's route is HEVC with alpha, documented in its HEVC Video with Alpha Interoperability Profile. There is no single transparent video file that covers every browser, so plan a non-transparent fallback.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss

Related Articles