Skip to content
MIME Types: Complete Reference for Developers

MIME Types: Complete Reference for Developers

DodaTech Updated Jun 20, 2026 7 min read

MIME (Multipurpose Internet Mail Extensions) types are labels that tell browsers and applications what type of content a file or response contains — enabling correct parsing, rendering, and handling.

What You’ll Learn

By the end of this tutorial, you’ll understand what MIME types are, common types for web development, how servers determine types (extension vs magic bytes), configuring MIME types in web servers, security considerations including MIME sniffing, and best practices. Prerequisites: HTTP Protocol basics.

Why It Matters

When a browser receives a file, it looks at the Content-Type header to decide how to handle it. Wrong MIME types cause broken downloads, security vulnerabilities, and rendering failures.

Real-World Use

You visit a site that serves an image. The server sends Content-Type: image/png. The browser knows this is a PNG and renders it. If the server sent Content-Type: text/plain, the browser would display raw binary garbage.

How MIME Types Work


flowchart LR
  A[Server Response] --> B[Content-Type Header]
  B --> C{Type Check}
  C -->|text/html| D[Render HTML]
  C -->|image/jpeg| E[Display Image]
  C -->|application/pdf| F[Open PDF Viewer]
  C -->|application/zip| G[Download File]
  C -->|text/javascript| H[Execute Script]

Prerequisites: HTTP Protocol fundamentals, Content Negotiation basics.

MIME Type Structure

A MIME type has two parts: type and subtype, separated by a slash.

type/subtype
# MIME type structure
mime_types = {
    'text/html': {
        'type': 'text',
        'subtype': 'html',
        'description': 'HTML documents'
    },
    'application/json': {
        'type': 'application',
        'subtype': 'json',
        'description': 'JSON data'
    },
    'image/png': {
        'type': 'image',
        'subtype': 'png',
        'description': 'PNG images'
    },
}

for mime, info in mime_types.items():
    print(f"{mime:30} → type={info['type']:12} subtype={info['subtype']:6} ({info['description']})")

Expected output:

text/html                      → type=text        subtype=html   (HTML documents)
application/json               → type=application subtype=json   (JSON data)
image/png                      → type=image       subtype=png    (PNG images)

Common MIME Types Reference

common_types = {
    # Text
    'text/html': '.html, .htm',
    'text/css': '.css',
    'text/javascript': '.js (modern; was application/javascript)',
    'text/plain': '.txt',
    'text/csv': '.csv',

    # Application
    'application/json': '.json',
    'application/xml': '.xml',
    'application/pdf': '.pdf',
    'application/zip': '.zip',
    'application/gzip': '.gz, .tgz',
    'application/octet-stream': 'Generic binary (use when no other type fits)',
    'application/x-www-form-urlencoded': 'HTML form data (URL-encoded)',
    'multipart/form-data': 'HTML forms with file uploads',

    # Image
    'image/jpeg': '.jpg, .jpeg',
    'image/png': '.png',
    'image/gif': '.gif',
    'image/webp': '.webp',
    'image/svg+xml': '.svg',
    'image/x-icon': '.ico',

    # Audio/Video
    'audio/mpeg': '.mp3',
    'video/mp4': '.mp4',
    'video/webm': '.webm',

    # Font
    'font/woff2': '.woff2',
    'font/woff': '.woff',
}

print(f"{'MIME Type':35} {'Extensions'}")
print("-" * 60)
for mime, ext in common_types.items():
    print(f"{mime:35} {ext}")

Expected output:

MIME Type                           Extensions
------------------------------------------------------------
text/html                           .html, .htm
text/css                            .css
text/javascript                     .js (modern; was application/javascript)
text/plain                          .txt
text/csv                            .csv
application/json                    .json
application/xml                     .xml
application/pdf                     .pdf
application/zip                     .zip
application/gzip                    .gz, .tgz
application/octet-stream            Generic binary
application/x-www-form-urlencoded   HTML form data (URL-encoded)
multipart/form-data                 HTML forms with file uploads
image/jpeg                          .jpg, .jpeg
image/png                           .png
image/gif                           .gif
image/webp                          .webp
image/svg+xml                       .svg
image/x-icon                        .ico
audio/mpeg                          .mp3
video/mp4                           .mp4
video/webm                          .webm
font/woff2                          .woff2
font/woff                           .woff

How Servers Determine MIME Types

By File Extension

import mimetypes

# Python's built-in MIME type detection
files = [
    'index.html',
    'style.css',
    'app.js',
    'data.json',
    'image.jpg',
    'document.pdf',
    'archive.zip',
]

print(f"{'File':25} {'MIME Type':30} {'Encoding'}")
print("-" * 65)
for filename in files:
    mime_type, encoding = mimetypes.guess_type(filename)
    print(f"{filename:25} {str(mime_type):30} {encoding or 'none'}")

Expected output:

File                     MIME Type                      Encoding
-----------------------------------------------------------------
index.html               text/html                      none
style.css                text/css                       none
app.js                   text/javascript                none
data.json                application/json               none
image.jpg                image/jpeg                     none
document.pdf             application/pdf                none
archive.zip              application/zip                none

By Magic Bytes (Content Sniffing)

# Detect file type by magic bytes (first few bytes of file)
def detect_mime_by_magic_bytes(file_path):
    magic_bytes_signatures = [
        (b'\x89PNG\r\n\x1a\n', 'image/png'),
        (b'\xff\xd8\xff', 'image/jpeg'),
        (b'GIF87a', 'image/gif'),
        (b'GIF89a', 'image/gif'),
        (b'%PDF', 'application/pdf'),
        (b'PK\x03\x04', 'application/zip'),
        (b'{\n', 'application/json'),  # Simple heuristic
        (b'<html', 'text/html'),
        (b'<!DOCTYPE html', 'text/html'),
    ]

    try:
        with open(file_path, 'rb') as f:
            header = f.read(16)

        for signature, mime in magic_bytes_signatures:
            if header.startswith(signature):
                return mime
        return 'application/octet-stream'
    except FileNotFoundError:
        return 'file not found'

# Test with simulated data
simulated_files = {
    'image.png': b'\x89PNG\r\n\x1a\n' + b'...',
    'document.pdf': b'%PDF-1.4' + b'...',
    'archive.zip': b'PK\x03\x04' + b'...',
    'data.json': b'{\n  "name": "test"\n}',
}

for name, content in simulated_files.items():
    mime = 'image/png' if name.endswith('.png') else \
           'application/pdf' if name.endswith('.pdf') else \
           'application/zip' if name.endswith('.zip') else \
           'application/json'
    print(f"{name:20} detected as: {mime}")

Configuring MIME Types in Web Servers

Nginx

# /etc/nginx/mime.types
types {
    text/html                             html htm;
    text/css                              css;
    text/javascript                       js;
    application/json                      json;
    image/png                             png;
    image/jpeg                            jpeg jpg;
    image/webp                            webp;
    application/pdf                       pdf;
    font/woff2                            woff2;
}

Apache

# .htaccess or httpd.conf
AddType text/html .html
AddType application/json .json
AddType image/webp .webp
AddType font/woff2 .woff2

# Force download for specific types
AddType application/octet-stream .exe .zip

Python Flask

from flask import Flask, send_file, make_response

app = Flask(__name__)

@app.route('/files/<path:filename>')
def serve_file(filename):
    # Override MIME type if needed
    response = make_response(open(filename, 'rb').read())
    if filename.endswith('.custom'):
        response.headers['Content-Type'] = 'application/x-custom'
    else:
        response.headers['Content-Type'] = 'application/octet-stream'
    return response

Security Considerations

MIME Sniffing

Browsers sometimes ignore the Content-Type header and try to “sniff” the actual content type. This is a security risk:

# Preventing MIME sniffing
security_headers = {
    "X-Content-Type-Options": "nosniff",  # Don't sniff — use declared Content-Type
    "Content-Type": "text/html; charset=utf-8",
}

def apply_security_headers(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['Content-Security-Policy'] = "default-src 'self'"
    return response

print("Security headers applied:")
for header, value in security_headers.items():
    print(f"  {header}: {value}")

Expected output:

Security headers applied:
  X-Content-Type-Options: nosniff
  Content-Type: text/html; charset=utf-8

Dangerous MIME Types

MIME TypeRisk
text/htmlIf served for user-uploaded content, enables XSS
application/x-javascriptOld JS MIME — don’t use
text/javascriptEnsure only trusted scripts use this
application/octet-streamTriggers download — safe for unknown types

Common MIME Type Errors

1. Wrong MIME Type for JavaScript

Using application/x-javascript or text/javascript for modern JS. Use text/javascript and ensure your server maps .js to it.

2. Serving User Uploads as text/html

An attacker uploads an HTML file with <script> tags. If served as text/html, it executes in the browser. Always serve user uploads with generic or image/document MIME types.

3. Missing MIME Type Configuration

Server returns Content-Type: text/plain for CSS files. Browsers don’t apply the styles. Configure MIME types correctly on your server.

4. Not Using X-Content-Type-Options

Without this header, browsers may sniff content and ignore your Content-Type. An image containing HTML could be rendered as a page (polyglot attack).

5. Confusing multipart/form-data with application/x-www-form-urlencoded

Form data without files uses URL encoding. Forms with file uploads must use multipart/form-data. The enctype attribute must match.

Practice Questions

1. What is a MIME type? A label (type/subtype) that identifies the format of content sent over HTTP or email, enabling the client to handle it correctly.

2. How does a server determine the MIME type of a file? By file extension (most common) or by reading magic bytes (the first few bytes of the file content).

3. What does X-Content-Type-Options: nosniff do? It tells the browser to trust the declared Content-Type and not attempt to sniff the actual content type — preventing MIME-type confusion attacks.

4. Why is multipart/form-data used for file uploads? Because file data is binary and can’t be URL-encoded. Multipart splits the request into separate parts, each with its own Content-Type and headers.

5. Challenge: Build a MIME type validator Write a Python script that scans a directory, detects each file’s MIME type by both extension and magic bytes, and reports any mismatches.

FAQ

What's the difference between MIME type and file extension?
The MIME type is the authoritative format identifier sent in HTTP headers. The file extension is a convention that servers use to determine the MIME type. The header wins.
Can MIME types be spoofed?
Yes. A server can send Content-Type: image/png for a file that’s actually HTML. This is why browsers implemented MIME sniffing — and why X-Content-Type-Options is critical.
What is the correct MIME type for JSON?
application/json. Older APIs used text/json or text/x-json, but these are incorrect.
What MIME type should I use for uploaded files?
For known types, use the appropriate MIME. For unknown types, use application/octet-stream to force download and prevent browser rendering.

Try It Yourself

▶ Try It Yourself Edit the code and click Run

Mini Project: File Type Detector

Build a Python CLI tool that takes a file, reads its magic bytes, and identifies the actual MIME type — useful for verifying that uploaded files match their declared type. Security angle: Durga Antivirus Pro uses MIME type detection to identify malicious files that disguise executables as images or documents — cross-referencing the declared extension against the actual magic bytes.

What’s Next

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

What’s Next

Congratulations on completing this MIME Types tutorial! Here’s where to go from here:

  • Practice daily — Check Content-Type headers on every API response you build
  • Build a project — Create a file upload service with proper MIME validation
  • Explore related topics — Learn about HTTP Caching and Content Negotiation

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro