HTTP 内容类型对照表
常见 HTTP Content-Type(媒体类型)含义与示例,覆盖 JSON、HTML、表单、图片、音视频与文件下载,并说明 charset 与 multipart boundary 用法。
原理解析
Content-Type 响应头告诉客户端「返回的字节流是什么格式」,如 text/html、application/json、image/png。它决定了浏览器是渲染页面、解析 JSON 还是直接下载。
其值由 MIME 类型加可选参数组成,最常见是 charset(如 application/json; charset=utf-8)声明文本编码。注意它和文件扩展名无必然联系:服务器可把 .html 以 text/plain 返回,客户端只看 Content-Type。
实务上一条好规则:能写明具体子类型就写具体子类型。application/json 比笼统的 text/plain 更利于服务端解析、CDN 判断是否可压缩、浏览器决定是否触发下载。只在确实无法确定格式时才退到 text/plain 或 application/octet-stream,并在这种情况下补 Content-Disposition 让下游行为可预期。另外注意 GET 请求的 body 没有长度限制保证,某些服务端与代理会直接丢弃,与其指望它传数据,不如改用 POST 加 query 参数。
什么时候该查这张表
这张表在你要判断「请求体/响应体是什么格式」时有用:Content-Type 头是 HTTP 里最常被忽略却又最影响行为的头。它决定服务端怎么解析你的 body、浏览器怎么渲染响应、以及 CDN 该不该缓存。
不该查的情况:你在调「文件上传」或「静态资源」的类型。MIME 类型与文件扩展名的对应关系属于另一张表(站内「MIME 类型」页),而浏览器对未知类型的处理策略又另有一套。
判断标准:如果你的问题是「服务端为什么没解析我的 body」,九成是 Content-Type 缺失或写错,这时候查这张表;如果是「为什么返回 JSON 变成下载了」,那要查的是 Content-Disposition 与 nosniff,不是这里。
另一个不该查的信号:你打算给请求加 Content-Type 却说不出这个 body 是什么格式。加错类型比不加更糟——某些服务端会直接拒绝而不是忽略。
边界与常见陷阱
Content-Type 缺失时,服务端「猜」的方式各不相同
Express 的 body-parser 默认不解析任何 body(未声明类型时不初始化 req.body);某些框架会 fallback 成 application/octet-stream;PHP 则会按 $_FILES 与表单规则分别处理。同一份 JSON 在不同框架下症状完全不同:一个是 req.body 为 undefined,一个是解析抛错,一个是被当成纯文本。所以「传了 body 却读不到」优先查 Content-Type,而不是先怀疑框架。
charset 不是可有可无的修饰
HTTP 规范规定 application/json 的默认字符集是 UTF-8,但 text/* 类型的默认取决于实现,通常是 ISO-8859-1。实践中最稳的做法是始终显式写 charset,例如 application/json; charset=utf-8;发送非 ASCII 内容时更不能省。中文场景下漏写 charset 出现的乱码,往往在服务端而非浏览器端表现为解码异常。
带文件上传的表单不能用 application/json
multipart/form-data 是浏览器原生提交文件的方式,带 boundary 参数;它和 application/x-www-form-urlencoded 只能二选一。把文件塞进 JSON 再 base64 编码,体积会膨胀约 33%,且失去流式上传能力,大文件必然超限。需要结构化数据时,正确做法是 multipart 里同时带字段和文件,而不是把文件编码进 JSON。
Content-Type 不决定 CORS,但会与预检交互
CORS 失败常被误判为 Content-Type 问题。实际链路是:带自定义头(如 Authorization)或非常规 Content-Type 会触发 OPTIONS 预检,预检失败才是真正原因。application/json 对简单请求是「安全」的,但自定义 Authorization 头一定触发预检。排查时先看浏览器 Network 里有没有 OPTIONS 及其响应头,而不是反复改 Content-Type。
与相邻内容的分工
想查「文件扩展名 ↔ MIME 类型」的完整对应(.png、.woff2、.wasm 等)以及各类型能否内联展示,用站内「MIME 类型」页;本页只覆盖 HTTP 通信中的 Content-Type 语义。
响应为什么被浏览器当作下载而非渲染,除了 Content-Type 还要看 Content-Disposition 与 X-Content-Type-Options: nosniff,本表不覆盖这两者。
字符编码怎么从字节还原成文本,属于站内「字符编码」页;本页只涉及声明编码的方式(charset 参数)。
需要把 JSON 美化后查看内容,用站内 JSON 格式化工具;本页不提供 JSON 校验能力。
实战片段
要点:① 服务端要显式校验 Content-Type 并返回 415,而不是让解析器静默失败——这样「忘带类型」会立刻暴露在开发期;② charset=utf-8 显式写,别依赖默认值,text/* 类型的默认字符集是实现相关的;③ 涉及非 ASCII(中文、Emoji)时,缺 charset 的乱码往往在服务端解码阶段就发生,浏览器里看到的只是结果。
// Express:显式声明 charset,并统一错误响应
app.post('/api/echo', express.json({ limit: '1mb' }), (req, res) => {
// req.headers['content-type'] 形如 "application/json; charset=utf-8"
const ct = req.headers['content-type'] || ''
if (!ct.startsWith('application/json')) {
return res.status(415).json({
error: 'Unsupported Media Type',
got: ct || '(missing)',
})
}
res.json({ ok: true, keys: Object.keys(req.body ?? {}) })
})
// 前端:JSON 请求显式带 charset
await fetch('/api/echo', {
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=utf-8' },
body: JSON.stringify({ name: '张三', emoji: '🛠' }),
})实践注解
- charset 在 Content-Type 里,不在 HTML meta 里优先响应头 Content-Type: text/html; charset=utf-8 优先于 <meta charset>。若响应头声明了 GBK,页面里的 meta 说 UTF-8 也没用——这是「本地正常、线上乱码」的常见成因。
- multipart/form-data 的 boundary 必须与 body 一致上传表单的 Content-Type 带自动生成的 boundary 参数,手写请求时必须保证 body 的分隔符与之逐字节一致,并注意结尾的 --boundary-- 与 CRLF。少一个换行就会得到「解析不出文件」的模糊报错。
- Content-Type 与 Accept 是两件事Accept 是客户端声明「我能接收什么」,Content-Type 是发送方声明「我发的是什么」。服务端只看 Accept 就决定返回体,或客户端用 Content-Type 描述期望,都会造成协商失败。
数据来源
- RFC 9110 §8.3 (representation metadata)
- IANA Media Types Registry
- RFC 2046 (multipart)
上述编号、名称与定义均来自公开规范,本站不主张对规范数据本身的独创性;本页的编排方式、中文释义与实践注解由本站撰写。
| Content-Type | 含义 | 示例 |
|---|---|---|
| application/json | JSON 结构化数据 | Content-Type: application/json; charset=utf-8 |
| application/xml | XML 数据 | application/xml; charset=utf-8 |
| text/html | HTML 文档 | text/html; charset=utf-8 |
| text/plain | 纯文本 | text/plain; charset=utf-8 |
| application/x-www-form-urlencoded | HTML 表单默认编码 | application/x-www-form-urlencoded |
| multipart/form-data | 文件上传表单 | multipart/form-data; boundary=----WebKitFormBoundaryABC |
| application/octet-stream | 未知二进制流(触发下载) | application/octet-stream |
| image/png | PNG 图片 | image/png |
| image/jpeg | JPEG 图片 | image/jpeg |
| image/svg+xml | SVG 矢量图 | image/svg+xml |
| audio/mpeg | MP3 音频 | audio/mpeg |
| video/mp4 | MP4 视频 | video/mp4 |
| application/pdf | PDF 文档 | application/pdf |
| application/zip | ZIP 压缩包 | application/zip |
| font/woff2 | WOFF2 字体 | font/woff2 |
| application/javascript | JavaScript 脚本 | application/javascript; charset=utf-8 |
| application/ld+json | 结构化数据(JSON-LD) | application/ld+json |
| application/gzip | Gzip 压缩数据 | application/gzip |
常见问题
Content-Type 的 charset 不写会怎样?
服务端若省略 charset,浏览器会按自身默认编码猜测(常是 ISO-8859-1 或随 Locale),中文等 UTF-8 文本极易出现乱码。文本类类型强烈建议显式写 charset=utf-8。二进制类型(image/*、application/octet-stream)不需要 charset。
multipart/form-data 的 boundary 是什么?
boundary 是分隔表单各字段的分隔符,配合两个连字符前缀(如 --boundary)切分 parts。它由客户端随机生成,无需手写;服务端按 Boundary 解析各字段与文件。
application/json 和 text/json 用哪个?
标准与事实实践是 application/json(IANA 注册、被所有库识别)。text/json 非标准、部分旧系统或防火墙可能误判,不建议使用。
为什么下载接口常返回 application/octet-stream?
octet-stream 表示「未知二进制」,浏览器见到它通常不会尝试预览,而是直接触发下载(配合 Content-Disposition: attachment)。适合导出文件、安装包等场景。