Content Negotiation Security
MediumAccept, Content-Type, MIME Sniffing
Content negotiation headers (Accept, Content-Type) are attack surfaces. Servers that accept any Content-Type without validation, perform MIME sniffing, or reflect user-controlled Accept headers unsanitized are vulnerable to injection and information disclosure. X-Content-Type-Options: nosniff disables MIME sniffing.
Overview
Content negotiation is the process by which client and server agree on the format of exchanged data. The client sends Accept headers describing what it can handle; the server responds with Content-Type describing what it sent. This exchange creates several security concerns.
MIME sniffing: without X-Content-Type-Options: nosniff, browsers may ignore the declared Content-Type and guess the content type from the first bytes. An attacker can upload a file with a .jpg extension but with JavaScript content – if the server serves it with image/jpeg but the browser sniffs JavaScript, it can execute the uploaded script.
Content-Type validation: servers that accept requests without validating Content-Type can be confused. A JSON endpoint that processes application/x-www-form-urlencoded as JSON may be exploitable. Always validate that the Content-Type matches what the endpoint expects.
Content-Type injection: if user-controlled data appears in a Content-Type header without sanitization, attackers can inject header fields or MIME boundaries.
The Attack: MIME Sniffing Script Execution
Without nosniff, a browser may execute an uploaded image file as JavaScript if its content looks like a script, bypassing the server's declared Content-Type.
# Attacker uploads a file named avatar.jpg containing:
<script>alert(document.cookie)</script>
# Server responds with:
HTTP/1.1 200 OK
Content-Type: image/jpeg
# No X-Content-Type-Options header
# Browser sniffs first bytes, detects HTML/script, executes it
# Result: XSS via image upload bypassDefenses
1X-Content-Type-Options: nosniff
Tells the browser to respect the declared Content-Type and never sniff. Without this, browsers may execute JavaScript disguised as an image or text file. One header, zero tradeoffs.
X-Content-Type-Options: nosniff2Strict Content-Type validation on ingress
Validate the Content-Type of incoming requests against what the endpoint expects. Return 415 Unsupported Media Type if the Content-Type is absent or unexpected. Never auto-parse request bodies without type validation.
POST /api/users HTTP/1.1
Content-Type: text/plain ← unexpected for a JSON API
HTTP/1.1 415 Unsupported Media Type
{"error": "Content-Type must be application/json"}3Explicit Content-Type on all responses
Always set an explicit Content-Type on every response. Never serve dynamic content without a Content-Type – some frameworks omit it and leave it to browser sniffing, which is exploitable.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniffChecklist
- ✓Set X-Content-Type-Options: nosniff on every response, especially file serving endpoints
- ✓Validate Content-Type on all incoming requests before parsing the body
- ✓Return 415 Unsupported Media Type for requests with unexpected Content-Type
- ✓Always set an explicit Content-Type on every response
- ✓Scan uploaded files server-side – do not trust the Content-Type header from uploads
- ✓Set Content-Disposition: attachment for file downloads to prevent inline rendering
Related Headers
Related Status Codes
FAQ
Is X-Content-Type-Options: nosniff always safe to set?
Yes. nosniff has no negative side effects on correctly configured servers that always set accurate Content-Type headers. The only case where it could break something is if a server intentionally relies on browser sniffing (which it should not). Set it on every response.
What is the difference between Accept and Content-Type?
Accept is a request header – the client tells the server what formats it can handle. Content-Type describes the format of the actual message body, in both requests (body format) and responses (response format). They work together in content negotiation: client says 'I accept application/json', server responds with Content-Type: application/json.