MIME 类型(Content-Type)完全指南:扩展名怎么对、浏览器怎么处理
讲清 MIME 类型的 type/subtype 结构,常见 json、pdf、png、mp4 怎么对应,以及上传下载时 Content-Type 头怎么设、浏览器据此渲染还是下载,配可直接复制的查表工具。
MIME 类型(Content-Type)完全指南:扩展名怎么对、浏览器怎么处理
写后端接文件上传,或者配静态服务器的人,迟早会被一个问题绊住:同一份文件,有时浏览器内联渲染,有时弹出保存框,有时干脆当乱码铺满整个页面。决定这三种结局的,不是文件扩展名,而是一串叫 MIME 类型的短字符串。它在 HTTP 里以 Content-Type 头的形式出现,真正在协议里被传输的,是它,不是磁盘上那个 .pdf 后缀。
这篇把 MIME 类型从结构到落地讲一遍。
MIME 类型的结构:type/subtype
一个 MIME 类型由斜杠分成两段:type/subtype。前面的 type 是大类,后面的 subtype 是具体格式。
image/png:type 是 image(图片这一大类),subtype 是 png(具体的 PNG 格式)。application/pdf:type 是 application(应用层二进制数据),subtype 是 pdf。text/html:type 是 text(文本),subtype 是 html。
常见的 type 一共没几个:text、image、audio、video、application、multipart、font。subtype 才是真正区分格式的部分。subtype 里还可能带 vnd.(厂商私有)或 x-(非标准实验性)前缀,比如 xlsx 的 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,那一长串就是微软在 application 大类下注册的私有子类型。
文本类还会拖一个参数尾巴:text/html; charset=utf-8。分号后面的 charset=utf-8 告诉对方按 UTF-8 解码,缺了它,中文很容易变成乱码。
几个高频类型的对应关系
下面这几个是我自己每周都要确认的,直接背下来也值:
.json→application/json(规范强制 UTF-8,所以不用额外写 charset).pdf→application/pdf.png→image/png,.jpg→image/jpeg(注意是 jpeg 不是 jpg),.webp→image/webp.mp4→video/mp4,.mp3→audio/mpeg.csv→text/csv; charset=utf-8(中文列必须带 charset).js→text/javascript(不是老的application/javascript)
最后这条 .js 是个经典坑。WHATWG 的 HTML 和 mime-sniffing 标准已经把 text/javascript 定为 JavaScript 唯一正确的类型,application/javascript 和更老的 application/x-javascript 都是已废弃的别名。浏览器还容忍旧名,但严格的服务端和 linter 会报。新代码就发对的那个。要逐个核对扩展名和别名,直接查 MIME 类型查询表,双向都能查,还能一键复制完整的 Content-Type 头。
上传:Content-Type 设错,字节还没读就被拒
很多 API 在读第一个字节前就先校验 Content-Type。我遇到过的上传失败,八成是这三种之一:
- 发了
application/json,但接口要文件,得用multipart/form-data。 - 文件没扩展名或扩展名不认,浏览器猜了个笼统的
application/octet-stream,服务端白名单里没有它,直接 415 Unsupported Media Type。 - 写死了
application/javascript,服务端却只认text/javascript。
排查思路很固定:先看客户端实际发出的 MIME,再看接口期望的 MIME,对齐就行。这里有个更阴的情况:有人把 .png 改名成 .pdf 上传,扩展名是假的,字节才是真的。光看扩展名会被骗,得读文件开头的魔数签名才知道真实类型,这一步交给 文件 MIME 类型检查 来做,它读的是真实字节而不是文件名。
下载:Content-Type 加 Content-Disposition
下载这一侧,我踩过的最典型的坑是:API 流式返回一份 xlsx 报表,浏览器却当成乱码文本铺在页面上。原因是响应头里 Content-Type 写成了 text/plain。正确做法是两步:
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="report.xlsx"
前者告诉浏览器这是什么,后者明确"请当附件下载,别内联打开"。如果你就是想强制触发下载、又懒得查具体类型,发 application/octet-stream 是最稳的兜底,它几乎总会弹保存框。但反过来,有具体类型时别偷懒用 octet-stream,那会挡掉内联预览。
浏览器拿到 Content-Type 之后做什么
浏览器收到响应后,会综合 Content-Type 头、Content-Disposition 头和它自己的嗅探规则,决定三件事之一:内联渲染(image、text/html、application/pdf 这类它认识且能显示的)、调用插件或下载(它不认识的)、强制下载(看到 Content-Disposition: attachment)。
所以同一份 PDF,服务端发 application/pdf 时它在标签页里打开预览,发 application/octet-stream 时它弹保存框。配静态服务器时,如果 nginx 的 types {} 块里没给某个扩展名配映射,它就回退到 octet-stream,于是你的 .webp 图片莫名其妙变成了下载。补上映射,跑 nginx -t 再 reload 即可。WebAssembly 尤其挑剔,.wasm 必须发 application/wasm,否则流式编译直接失败。
调 HTTP 接口时,如果你还想顺手看懂 415、404 这些状态码各自意味着什么,可以配合 HTTP 状态码解释 一起查。
把 MIME 类型当成协议层的"文件身份证",而不是磁盘上那个可以随便改的后缀,上传下载这类问题就少了一大半。下次再遇到"浏览器为什么不渲染",先去看 Content-Type,基本一查一个准。
Made by Toolora · Updated 2026-06-13