Senger CodeLab πŸš€

How to check file MIME type with JavaScript before upload

September 29, 2026

πŸ“‚ Categories: Javascript
How to check file MIME type with JavaScript before upload

Ensuring the security and integrity of your web application often hinges on validating user-uploaded files. One crucial aspect of this validation is verifying the file’s MIME type using JavaScript before it even reaches your server. This pre-upload check prevents users from inadvertently or maliciously uploading incorrect file types, safeguarding your system and improving user experience. Knowing how to check file MIME type with JavaScript empowers you to control the types of files allowed, enhancing security and streamlining the upload process. This article provides a comprehensive guide to implementing robust client-side MIME type validation using JavaScript, along with best practices and practical examples.

Understanding MIME Types

MIME (Multipurpose Internet Mail Extensions) types are strings that identify the type of data being transmitted. They play a critical role in web applications, telling the browser and server how to handle a particular file. For instance, the MIME type “image/jpeg” indicates a JPEG image, while “application/pdf” signifies a PDF document. Accurate MIME type detection is essential for proper file handling and security. Mismatched MIME types can lead to errors, security vulnerabilities, and a frustrating user experience. A strong understanding of MIME types is fundamental for any web developer.

Client-Side MIME Type Validation with JavaScript

JavaScript provides several ways to check a file’s MIME type before it’s uploaded to the server. The most reliable method involves using the File object’s type property. This property returns the MIME type provided by the browser based on the file’s extension. While generally accurate, it’s important to note that this can be spoofed. Therefore, combining this check with server-side validation is highly recommended. This dual-layer approach ensures comprehensive security. Here’s an example:

<input type="file" id="fileInput" onchange="checkMimeType()"> <script> function checkMimeType() { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; if (file.type === 'image/jpeg' || file.type === 'image/png') { console.log('Valid image file'); // Proceed with upload } else { console.log('Invalid file type'); // Display error message to the user } } </script> 

Beyond the Basics: Advanced Techniques

For enhanced security, consider employing the FileReader API to examine the file’s magic number. The magic number is a unique sequence of bytes at the beginning of a file that identifies its type, regardless of the file extension. This adds another layer of validation, making it significantly harder for malicious users to bypass your security measures. By checking both the type property and the magic number, you can create a more robust and secure file upload process.

Using the FileReader API

The FileReader API allows you to read the contents of a file, which can be used to determine the magic number. Here’s an example:

const reader = new FileReader(); reader.onloadend = function(e) { const arr = (new Uint8Array(e.target.result)).subarray(0, 4); let header = ""; for(let i = 0; i < arr.length; i++) { header += arr[i].toString(16); } // Compare header with known magic numbers }; reader.readAsArrayBuffer(file); 

Server-Side Validation: A Critical Component

Client-side validation enhances user experience by providing immediate feedback. However, it should never be relied upon as the sole security measure. Server-side validation is crucial for ensuring data integrity and protecting your system from malicious uploads. Always validate MIME types and file content on the server before processing or storing any uploaded files. This redundancy provides a comprehensive security approach. Visit MDN Web Docs for further reading on MIME types. Learn more about server-side validation techniques.

  • Always validate MIME types on both the client and server.
  • Consider using magic number detection for enhanced security.
  1. Select the file using an input element.
  2. Access the file object using JavaScript.
  3. Check the file’s type property.
  4. Optionally, use the FileReader API to verify the magic number.

Featured Snippet: To quickly check a file’s MIME type in JavaScript, use the file.type property. Remember that this is a client-side check and should always be complemented by server-side validation for maximum security.

[Infographic Placeholder] - Regularly update your MIME type validation logic to address new vulnerabilities.

  • Educate users about acceptable file types to prevent unnecessary uploads.

Handling Different Browsers

While the file.type property is widely supported, subtle differences exist between browsers. Ensure your code handles these variations gracefully to provide a consistent user experience across different platforms. Testing your implementation thoroughly across various browsers is crucial for identifying and resolving compatibility issues. Refer to browser documentation for specific details on file handling and MIME type detection.

See more information on W3Schools. Also check out IANA’s official list of MIME types for a comprehensive reference. FAQs

Q: Is client-side validation enough for security?

A: No, client-side validation should always be combined with server-side validation for robust security.

Q: How can I check for file extensions besides the MIME type?

A: You can extract the file extension from the file name and validate it against a whitelist of allowed extensions.

Implementing robust MIME type validation is essential for protecting your web application and ensuring a smooth user experience. By following the best practices outlined in this article and utilizing the provided code examples, you can create a secure and efficient file upload process. Remember to prioritize both client-side and server-side validation for comprehensive security. Start implementing these techniques today to bolster your web application’s security and improve user trust.

Question & Answer :
I have read this and this questions which seems to suggest that the file MIME type could be checked using JavaScript on client side. Now, I understand that the real validation still has to be done on server side. I want to perform a client side checking to avoid unnecessary wastage of server resource.

To test whether this can be done on client side, I changed the extension of a JPEG test file to .png and choose the file for upload. Before sending the file, I query the file object using a JavaScript console:

document.getElementsByTagName('input')[0].files[0]; 

This is what I get on Chrome 28.0:

File {webkitRelativePath: “”, lastModifiedDate: Tue Oct 16 2012 10:00:00 GMT+0000 (UTC), name: “test.png”, type: “image/png”, size: 500055…}

It shows type to be image/png which seems to indicate that the checking is done based on file extension instead of MIME type. I tried Firefox 22.0 and it gives me the same result. But according to the W3C spec, MIME Sniffing should be implemented.

Am I right to say that there is no way to check the MIME type with JavaScript at the moment? Or am I missing something?

You can easily determine the file MIME type with JavaScript’s FileReader before uploading it to a server. I agree that we should prefer server-side checking over client-side, but client-side checking is still possible. I’ll show you how and provide a working demo at the bottom.


Check that your browser supports both File and Blob. All major ones should.

if (window.FileReader && window.Blob) { // All the File APIs are supported. } else { // File and Blob are not supported } 

Step 1:

You can retrieve the File information from an <input> element like this (ref):

<input type="file" id="your-files" multiple> <script> var control = document.getElementById("your-files"); control.addEventListener("change", function(event) { // When the control has changed, there are new files var files = control.files, for (var i = 0; i < files.length; i++) { console.log("Filename: " + files[i].name); console.log("Type: " + files[i].type); console.log("Size: " + files[i].size + " bytes"); } }, false); </script> 

Here is a drag-and-drop version of the above (ref):

<div id="your-files"></div> <script> var target = document.getElementById("your-files"); target.addEventListener("dragover", function(event) { event.preventDefault(); }, false); target.addEventListener("drop", function(event) { // Cancel default actions event.preventDefault(); var files = event.dataTransfer.files, for (var i = 0; i < files.length; i++) { console.log("Filename: " + files[i].name); console.log("Type: " + files[i].type); console.log("Size: " + files[i].size + " bytes"); } }, false); </script> 

Step 2:

We can now inspect the files and tease out headers and MIME types.

✘ Quick method

You can naΓ―vely ask Blob for the MIME type of whatever file it represents using this pattern:

var blob = files[i]; // See step 1 above console.log(blob.type); 

For images, MIME types come back like the following:

image/jpeg
image/png
…

Caveat: The MIME type is detected from the file extension and can be fooled or spoofed. One can rename a .jpg to a .png and the MIME type will be be reported as image/png.


βœ“ Proper header-inspecting method

To get the bonafide MIME type of a client-side file we can go a step further and inspect the first few bytes of the given file to compare against so-called magic numbers. Be warned that it’s not entirely straightforward because, for instance, JPEG has a few “magic numbers”. This is because the format has evolved since 1991. You might get away with checking only the first two bytes, but I prefer checking at least 4 bytes to reduce false positives.

Example file signatures of JPEG (first 4 bytes):

FF D8 FF E0 (SOI + ADD0)
FF D8 FF E1 (SOI + ADD1)
FF D8 FF E2 (SOI + ADD2)

Here is the essential code to retrieve the file header:

var blob = files[i]; // See step 1 above var fileReader = new FileReader(); fileReader.onloadend = function(e) { var arr = (new Uint8Array(e.target.result)).subarray(0, 4); var header = ""; for(var i = 0; i < arr.length; i++) { header += arr[i].toString(16); } console.log(header); // Check the file signature against known types }; fileReader.readAsArrayBuffer(blob); 

You can then determine the real MIME type like so (more file signatures here and here):

switch (header) { case "89504e47": type = "image/png"; break; case "47494638": type = "image/gif"; break; case "ffd8ffe0": case "ffd8ffe1": case "ffd8ffe2": case "ffd8ffe3": case "ffd8ffe8": type = "image/jpeg"; break; default: type = "unknown"; // Or you can use the blob.type as fallback break; } 

Accept or reject file uploads as you like based on the MIME types expected.


Demo

Here is a working demo for local files and remote files (I had to bypass CORS just for this demo). Open the snippet, run it, and you should see three remote images of different types displayed. At the top you can select a local image or data file, and the file signature and/or MIME type will be displayed.

Notice that even if an image is renamed, its true MIME type can be determined. See below.

Screenshot

Expected output of demo


``` // Return the first few bytes of the file as a hex string function getBLOBFileHeader(url, blob, callback) { var fileReader = new FileReader(); fileReader.onloadend = function(e) { var arr = (new Uint8Array(e.target.result)).subarray(0, 4); var header = ""; for (var i = 0; i < arr.length; i++) { header += arr[i].toString(16); } callback(url, header); }; fileReader.readAsArrayBuffer(blob); } function getRemoteFileHeader(url, callback) { var xhr = new XMLHttpRequest(); // Bypass CORS for this demo - naughty, Drakes xhr.open('GET', '//cors-anywhere.herokuapp.com/' + url); xhr.responseType = "blob"; xhr.onload = function() { callback(url, xhr.response); }; xhr.onerror = function() { alert('A network error occurred!'); }; xhr.send(); } function headerCallback(url, headerString) { printHeaderInfo(url, headerString); } function remoteCallback(url, blob) { printImage(blob); getBLOBFileHeader(url, blob, headerCallback); } function printImage(blob) { // Add this image to the document body for proof of GET success var fr = new FileReader(); fr.onloadend = function() { $("hr").after($("").attr("src", fr.result)) .after($("
").text("Blob MIME type: " + blob.type)); }; fr.readAsDataURL(blob); } // Add more from http://en.wikipedia.org/wiki/List_of_file_signatures function mimeType(headerString) { switch (headerString) { case "89504e47": type = "image/png"; break; case "47494638": type = "image/gif"; break; case "ffd8ffe0": case "ffd8ffe1": case "ffd8ffe2": type = "image/jpeg"; break; default: type = "unknown"; break; } return type; } function printHeaderInfo(url, headerString) { $("hr").after($("
").text("Real MIME type: " + mimeType(headerString))) .after($("
").text("File header: 0x" + headerString)) .after($("
").text(url)); } /* Demo driver code */ var imageURLsArray = ["http://media2.giphy.com/media/8KrhxtEsrdhD2/giphy.gif", "http://upload.wikimedia.org/wikipedia/commons/e/e9/Felis_silvestris_silvestris_small_gradual_decrease_of_quality.png", "http://static.giantbomb.com/uploads/scale_small/0/316/520157-apple_logo_dec07.jpg"]; // Check for FileReader support if (window.FileReader && window.Blob) { // Load all the remote images from the urls array for (var i = 0; i < imageURLsArray.length; i++) { getRemoteFileHeader(imageURLsArray[i], remoteCallback); } /* Handle local files */ $("input").on('change', function(event) { var file = event.target.files[0]; if (file.size >= 2 * 1024 * 1024) { alert("File size must be at most 2MB"); return; } remoteCallback(escape(file.name), file); }); } else { // File and Blob are not supported $("hr").after( $("
").text("It seems your browser doesn't support FileReader") ); } /* Drakes, 2015 */ ```
img { max-height: 200px } div { height: 26px; font: Arial; font-size: 12pt } form { height: 40px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <form> <input type="file" /> <div>Choose an image to see its file signature.</div> </form> <hr/>