A file called reaction.gif can still contain MP4 video. This matters in upload widgets: choosing an <img> solely from the filename can produce a broken preview, while accepting the file as a verified image can mislead later processing. A short header check can improve the error message before the application hands the bytes to a real decoder.
Read a small prefix, not the whole file
The following function checks the two GIF signatures and a common ISO Base Media File Format header. Its iso-bmff result deliberately does not claim that the file is a playable MP4. Other formats use the same container family.
async function inspectHeader(file) {
const bytes = new Uint8Array(await file.slice(0, 32).arrayBuffer());
const ascii = (start, length) =>
String.fromCharCode(...bytes.slice(start, start + length));
if (bytes.length >= 6 &&
['GIF87a', 'GIF89a'].includes(ascii(0, 6))) {
return { kind: 'gif', mimeHint: 'image/gif' };
}
if (bytes.length >= 12 && ascii(4, 4) === 'ftyp') {
return { kind: 'iso-bmff', majorBrand: ascii(8, 4) };
}
return { kind: 'unknown' };
}
The six-byte GIF marker is enough for this classification step. It does not establish that every subsequent block is valid, that there is more than one frame, or that decoding the image will fit the device's memory budget. A header check is a routing hint; the decoder and server-side validation still own acceptance.
For an ISO BMFF result, the four bytes after ftyp identify a major brand. Treating every brand as video/mp4 would be an overstatement. An application that accepts only GIF should explain that it found a container from another format family and ask for a converted file.
Keep filename and content type as separate evidence
A file input's accept="image/gif" setting helps users choose a file. It does not prove its contents. The filename extension and File.type can both be useful hints, but the upload endpoint should validate the actual supported format independently.
A UI can show a specific diagnosis without trying to repair the input automatically:
const info = await inspectHeader(file);
if (info.kind === 'iso-bmff') {
message.textContent =
'This looks like a video container. Export a GIF before uploading here.';
} else if (info.kind === 'unknown') {
message.textContent = 'This file does not have a recognized GIF header.';
} else {
message.textContent = 'GIF header found. Checking the image next…';
}
Avoid claiming that renaming fixes the file. Changing .mp4 to .gif changes the label, not the encoding. The inverse mistake also occurs when a tool labels every looping animation as a GIF even though it delivers video.
Apply the distinction to a saved X animation
The Twitter GIF Downloader page offers a concrete case: it describes the looping MP4 representation used for X animations and offers a real GIF output as well as MP4. This is a promotional link included in an article prepared for that tool; it is not an independent benchmark or a claim that this sample is its implementation.
If a destination accepts only images, select GIF output and inspect the downloaded file. If the destination supports video and playback controls are useful, retaining MP4 avoids an unnecessary format conversion. The receiving application should decide from the actual file, not from the word “GIF” in a post or button.
Use a media element only after choosing a supported path
A GIF preview belongs in an image element once the image decoder accepts it. A video container needs a supported video codec and container combination; the header alone cannot answer that. MDN's video element reference explains sources and playback behavior.
For untrusted uploads, enforce file size, decoded dimensions, resource limits, and a supported-format policy on the server too. The browser hint improves feedback, while the upload pipeline decides what it can safely process.
Top comments (0)