Request and response header
< Content-Type: application/json; charset=utf-8Content-Type Header: Values, Examples, charset
Content-Type tells the receiver what the body is: application/json, text/html; charset=utf-8, multipart/form-data. Common values, examples and 415 fixes.
- Direction
- Request and response
- Category
- Representation
- Spec
- RFC 9110 §8.3
- JS can set it
- Yes
- CORS-safelisted
- Only some values
TL;DR:
Content-Typenames the media type of the body. Sendapplication/jsonwith JSON, let your HTTP client set themultipart/form-databoundary, and addcharset=utf-8totext/*types; wrong or missing values cause 415 errors and blocked scripts.
Syntax
Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx3ZQ
A media type is type/subtype, optionally followed by ; parameter=value. Types are case-insensitive. charset is meaningful for text/* types; boundary is required for multipart/*.
Common values
| Content-Type | Used for |
|---|---|
text/html; charset=utf-8 | Web pages |
text/plain; charset=utf-8 | Plain text |
text/css, text/javascript | Stylesheets and scripts (RFC 9239 makes text/javascript the one to use) |
application/json | JSON APIs |
application/problem+json | RFC 9457 error bodies |
application/x-www-form-urlencoded | Default HTML form posts |
multipart/form-data | File uploads |
application/octet-stream | Unknown binary; browsers download |
application/pdf, application/zip | Documents and archives |
image/png, image/webp, image/svg+xml, video/mp4 | Media |
Requests
POST /api/users HTTP/1.1
Content-Type: application/json
{"name":"Alice"}
With fetch and FormData, do not set the header yourself. The browser adds the boundary:
// Right: browser sets multipart/form-data; boundary=...
fetch('/upload', { method: 'POST', body: formData })
// Wrong: the boundary is missing and the server cannot split the parts
fetch('/upload', { method: 'POST', body: formData, headers: { 'Content-Type': 'multipart/form-data' } })
With curl, -d defaults to application/x-www-form-urlencoded, which is why JSON posts fail until you add -H 'Content-Type: application/json':
curl -i https://api.example.com/users -d '{"name":"Alice"}'
# HTTP/1.1 415 Unsupported Media Type (or 400 with an empty body)
curl -i https://api.example.com/users -H 'Content-Type: application/json' -d '{"name":"Alice"}'
One CORS note: Content-Type: application/json is not a safelisted value for cross-origin requests, so it triggers a preflight. Only application/x-www-form-urlencoded, multipart/form-data and text/plain are exempt. See CORS preflight.
Errors people search for
Refused to execute script from 'https://example.com/app.js' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled. The path returned your SPA fallback or a 404 page. Check the actual response with curl -sI, fix the asset path or try_files/rewrite rule, and keep X-Content-Type-Options: nosniff on.
Refused to apply style from '...' because its MIME type ('text/html') is not a supported stylesheet MIME type. Same cause for CSS.
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/octet-stream". Serve .js and .mjs as text/javascript. In nginx check include mime.types; is present; in S3 or GCS set the object metadata Content-Type.
415 Unsupported Media Type. The server did not accept the request body type. See 415.
Mojibake such as é. A text/html response without charset is guessed by the browser. Send charset=utf-8 and also <meta charset="utf-8"> in the first 1024 bytes.
Download instead of render. application/octet-stream or a missing type with Content-Disposition: attachment. See Content-Disposition.
Sniffing
If the type is missing or generic, browsers may guess from the content (MIME sniffing), a vector for XSS through uploaded files served as HTML. X-Content-Type-Options: nosniff disables sniffing for scripts and styles and is worth setting everywhere; see X-Content-Type-Options. Serve user uploads from a separate domain with the correct type, or with Content-Disposition: attachment.
Server configuration
nginx maps extensions in mime.types and falls back to default_type:
include /etc/nginx/mime.types;
default_type application/octet-stream;
types { text/javascript mjs; }
Apache: AddType text/javascript .mjs. Verify with:
curl -sI https://example.com/app.mjs | grep -i content-type
Related
In practice
Express.js
// JSON: res.json() sets Content-Type automatically
app.get('/api/users', (req, res) => {
res.json({ users: [] })
// Content-Type: application/json; charset=utf-8
})
// Parse only the types you expect; others skip the body parser
app.use(express.json({ type: 'application/json' }))
// File download
app.get('/report', (req, res) => {
res.set('Content-Type', 'application/pdf')
res.set('Content-Disposition', 'attachment; filename="report.pdf"')
res.send(pdfBuffer)
})Next.js App Router
// Response.json() sets Content-Type: application/json
export async function GET() {
return Response.json({ users: [] })
}
// Custom type
export async function GET() {
return new Response('<feed></feed>', {
headers: { 'Content-Type': 'application/atom+xml; charset=utf-8' }
})
}Django
from django.http import JsonResponse, HttpResponse
def api_view(request):
return JsonResponse({'users': []}) # application/json
def feed_view(request):
return HttpResponse('<feed></feed>', content_type='application/atom+xml; charset=utf-8')Frequently asked questions
What is the Content-Type header?
It names the media type of the message body, optionally with parameters such as charset or boundary, so the receiver knows how to parse it. In a request it describes what you send; in a response it describes what the server returns.
What Content-Type should I use for JSON APIs?
Use application/json. RFC 8259 defines no charset parameter for it because JSON is always UTF-8 on the wire, so adding charset=utf-8 is harmless but unnecessary. Frameworks that emit it, such as Express res.json(), are not wrong.
What is the difference between application/x-www-form-urlencoded and multipart/form-data?
urlencoded sends key=value pairs with percent-encoding and is the default for HTML forms. multipart/form-data splits the body into parts separated by a boundary and is required for file uploads. Use a library to build multipart bodies: the Content-Type must include the generated boundary parameter.
Why does my server return 415 Unsupported Media Type?
The request Content-Type is not one the endpoint accepts. Typical causes are posting JSON without Content-Type: application/json, sending text/plain, or forcing a manual multipart header that lacks the boundary. See the 415 page for fixes.
Why does the browser say "Refused to execute script ... MIME type text/html"?
The script URL returned HTML, usually a 404 page or SPA fallback index.html, and the response carried X-Content-Type-Options: nosniff, so the browser refuses to treat it as JavaScript. Fix the path or the server rewrite rule so the file is actually served with a JavaScript type.
Should I use application/javascript or text/javascript?
Use text/javascript. RFC 9239 made it the sole recommended type and obsoleted the application/javascript preference; browsers accept both.
Sources
Related
X-Robots-Tag Header: noindex for PDFs and Staging
X-Robots-Tag sends noindex and snippet rules as an HTTP header for PDFs, images and staging sites. nginx, Apache and Cloudflare config, and the robots.txt trap.
MIME Type (Media Type)
A MIME type, or media type, is the type/subtype label in Content-Type that tells clients how to interpret a body. Covers nosniff and blocked-script errors.
Connection Header
Learn how the Connection header controls whether HTTP connections stay open (keep-alive) or close after each request. Optimize with persistent connections.
What Is HTTP? How the HTTP Protocol Works
HTTP is the request-response protocol behind every web page and API. See a real request and response, then methods, headers, status codes and what HTTPS adds.