MIME Types: Complete Reference for Developers
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]
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 .woffHow 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 noneBy 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 .zipPython 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 responseSecurity 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-8Dangerous MIME Types
| MIME Type | Risk |
|---|---|
text/html | If served for user-uploaded content, enables XSS |
application/x-javascript | Old JS MIME — don’t use |
text/javascript | Ensure only trusted scripts use this |
application/octet-stream | Triggers 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
Try It Yourself
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