Skip to content
>_ TrueFileSize.com
··7 min read

Fix Unsupported File Type Errors — MIME Type & Extension Validation

"Unsupported file type", "Invalid file format", "This file type is not allowed" — these errors happen when your application rejects a file that the user thinks should be accepted. Here's how file type detection works and how to fix it.

Common Error Messages

  • "Unsupported file type. Please upload a PDF, JPG, or PNG."
  • "Invalid file format"
  • "Error: MIME type application/octet-stream not allowed"
  • "The selected file type is not supported"
  • "File validation failed: expected image/*, got application/pdf"

How File Type Detection Works

There are three ways to identify a file's type. Each has trade-offs:

| Method | Checks | Reliable? | Spoofable? | |--------|--------|-----------|------------| | File extension | .pdf, .jpg | No | Yes (just rename) | | MIME type (Content-Type) | image/jpeg | Mostly | Yes (can be set to anything) | | Magic bytes (file signature) | FF D8 FF = JPEG | Yes | Hard (must modify binary) |

Best practice: Check all three. Extension for user display, MIME for quick filter, magic bytes for security.

Fix 1: Extension-Based Validation

// Basic extension whitelist
const ALLOWED = ['.pdf', '.jpg', '.jpeg', '.png', '.gif', '.webp'];

function validateExtension(filename) {
  const ext = '.' + filename.split('.').pop().toLowerCase();
  if (!ALLOWED.includes(ext)) {
    throw new Error(`Unsupported file type: ${ext}`);
  }
}

Problem: Users can rename malware.exe to malware.pdf. Never rely on extension alone.

Fix 2: MIME Type Validation

// Server-side (multer)
const upload = multer({
  fileFilter: (req, file, cb) => {
    const allowed = ['image/jpeg', 'image/png', 'application/pdf'];
    if (allowed.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error(`Unsupported MIME type: ${file.mimetype}`));
    }
  },
});

// Client-side (HTML)
<input type="file" accept=".pdf,.jpg,.png,image/*" />

Problem: MIME type comes from the browser, which gets it from the OS file association. Rename a .exe to .jpg and many browsers will send image/jpeg.

Fix 3: Magic Bytes Validation (Most Secure)

// Read first bytes of uploaded file and check signature
const FILE_SIGNATURES = {
  'image/jpeg': [0xFF, 0xD8, 0xFF],
  'image/png':  [0x89, 0x50, 0x4E, 0x47],
  'application/pdf': [0x25, 0x50, 0x44, 0x46], // %PDF
  'image/gif': [0x47, 0x49, 0x46, 0x38],       // GIF8
  'image/webp': [0x52, 0x49, 0x46, 0x46],       // RIFF
  'application/zip': [0x50, 0x4B, 0x03, 0x04],
};

function detectFileType(buffer) {
  for (const [mime, sig] of Object.entries(FILE_SIGNATURES)) {
    if (sig.every((byte, i) => buffer[i] === byte)) {
      return mime;
    }
  }
  return null; // Unknown type
}

// Usage in upload handler
app.post('/upload', upload.single('file'), (req, res) => {
  const buffer = fs.readFileSync(req.file.path);
  const detectedType = detectFileType(buffer);

  if (!detectedType) {
    fs.unlinkSync(req.file.path);
    return res.status(415).json({ error: 'Unrecognized file type' });
  }

  if (detectedType !== req.file.mimetype) {
    console.warn(`MIME mismatch: header=${req.file.mimetype}, actual=${detectedType}`);
  }
});

Fix 4: Using file-type Library (Recommended)

import { fileTypeFromBuffer } from 'file-type';

app.post('/upload', upload.single('file'), async (req, res) => {
  const buffer = fs.readFileSync(req.file.path);
  const type = await fileTypeFromBuffer(buffer);

  if (!type) {
    return res.status(415).json({ error: 'Could not detect file type' });
  }

  const allowed = ['image/jpeg', 'image/png', 'application/pdf'];
  if (!allowed.includes(type.mime)) {
    return res.status(415).json({
      error: `File type ${type.mime} (${type.ext}) is not allowed`,
    });
  }
});

Test with Sample Files

Use our MIME Type Lookup Tool to find correct MIME types and magic bytes.

| Test | File | Expected Behavior | |------|------|-------------------| | Valid PDF | sample-1mb.pdf | Accepted | | Valid image | sample-500kb.jpg | Accepted | | MIME mismatch | sample-jpg-as-pdf.pdf | Rejected (JPEG data in .pdf extension) | | Corrupted | sample-corrupt.pdf | Rejected or warning | | Zero byte | sample-zero-byte.bin | Rejected |

Whitelist vs Blacklist

Always whitelist (allow specific types), never blacklist (block specific types):

// WRONG — blacklist (attacker finds new extension)
const BLOCKED = ['.exe', '.bat', '.sh', '.php'];

// RIGHT — whitelist (only known-safe types allowed)
const ALLOWED = ['image/jpeg', 'image/png', 'application/pdf'];

For a complete MIME type reference, use our MIME Types Cheat Sheet.