MIME 类型完全指南:浏览器嗅探、安全策略与现代格式兼容性实战

为什么 MIME 类型比想象的重要

很多开发者把 MIME 类型当成「设置一次就不用管」的细节。但真实生产环境里,这些事故都和 MIME 配置直接相关:

  • 上传的图片被浏览器当 HTML 渲染,触发 XSS
  • WebP 图片在 Safari 显示白屏(早期 Safari 不支持 image/webp
  • .webp 文件被服务器当成 application/octet-stream 触发下载而非内联显示
  • CSS 文件被服务器返回 text/plain,浏览器拒绝应用样式
  • 用户把 .exe 改名为 .jpg 上传,前端 accept=".jpg" 校验通过

这些问题的根因都是 MIME 类型识别与浏览器行为理解不到位。本文从实战角度讲透 MIME 类型的浏览器兼容、安全策略与配置方法。

配套工具:MIME 类型查询工具。内置 100+ 常见扩展名与 MIME 对照表,覆盖 8 大类别,支持搜索与一键复制。

一、MIME 类型在 Web 中的角色

1.1 MIME 的双重身份

MIME 类型(Multipurpose Internet Mail Extensions)最初为邮件附件设计,后被 HTTP 协议采用,成为 Web 的标准文件格式标识:

┌─────────────────────────────────────────────────────┐
│  HTTP 传输时                                         │
│  服务器返回 Content-Type: image/webp                │
│  ──> 浏览器按 image/webp 渲染                       │
│                                                     │
│  本地文件系统                                       │
│  操作系统按扩展名关联应用打开                       │
│  ──> .webp 关联到图片查看器                         │
└─────────────────────────────────────────────────────┘

关键认知

  • HTTP 传输时,浏览器以 Content-Type 为准,而非扩展名
  • 本地文件以扩展名关联应用,但浏览器有 MIME 嗅探机制可推翻 Content-Type
  • 同一扩展名可能对应多个 MIME 类型(如 .ts 既可能是 TypeScript 也可能是 MPEG-TS)
  • 同一 MIME 类型也可能对应多个扩展名(如 image/jpeg 对应 .jpg.jpeg

1.2 MIME 类型格式

形如 type/subtype; parameters

示例含义
text/htmlHTML 文档
text/html; charset=utf-8HTML 文档,字符集 UTF-8
image/pngPNG 图片
application/jsonJSON 数据
multipart/form-data; boundary=---xyz表单上传,含分界符

type 是大类(如 text/image/application/audio/video/font),subtype 是具体类型,parameters 是可选参数(最常见的是 charset)。

二、8 大类别全览与高频类型

本站 MIME 查询工具 内置 100+ 类型,按用途分 8 大类:

类别高频扩展名关键 MIME
文档txt/html/css/csv/md/pdf/doc/xls/ppttext/plain text/html text/css text/csv text/markdown application/pdf
图片jpg/png/gif/webp/svg/bmp/ico/heic/avifimage/jpeg image/png image/gif image/webp image/svg+xml image/avif image/heic
音频mp3/wav/ogg/flac/aac/m4a/opusaudio/mpeg audio/wav audio/ogg audio/flac audio/aac audio/opus
视频mp4/webm/avi/mov/mkv/3gpvideo/mp4 video/webm video/x-msvideo video/quicktime video/x-matroska
压缩zip/rar/7z/tar/gz/bz2/xzapplication/zip application/vnd.rar application/x-7z-compressed application/gzip
代码js/ts/py/java/go/rs/sql/yaml/json/xmltext/javascript application/typescript text/x-python application/json application/xml application/yaml
字体ttf/otf/woff/woff2/eotfont/ttf font/otf font/woff font/woff2 application/vnd.ms-fontobject
应用exe/dll/apk/ipa/dmg/deb/rpm/jarapplication/octet-stream application/vnd.android.package-archive application/java-archive

2.1 几个值得注意的细节

  • .js 的 MIME 在 2020 年从 application/javascript 改为 text/javascriptRFC 9239)。现代浏览器两者都接受,但新配置应使用 text/javascript
  • .json 标准是 application/json,不是 text/json(虽然部分服务器接受)
  • .svgimage/svg+xml,注意 +xml 后缀,表示 XML 子类型
  • .woff2font/woff2RFC 8081 统一字体 MIME 为 font/*),早期配置可能写成 application/font-woff2
  • .webpimage/webp.avifimage/avif.heicimage/heic

三、浏览器 MIME 嗅探机制与安全风险

3.1 什么是 MIME 嗅探

当服务器返回 Content-Type: application/octet-stream(或未设置 Content-Type),浏览器会嗅探文件内容前几个字节判断真实类型:

服务器返回:Content-Type: application/octet-stream
浏览器嗅探:前 8 字节是 89 50 4E 47 0D 0A 1A 0A
浏览器判断:实际是 PNG
浏览器渲染:作为图片显示(而非触发下载)

3.2 嗅探的两面性

优点:用户体验好,避免「服务器配置错误导致文件下载」

风险:XSS 攻击向量。攻击者上传一个 .png 文件,文件内容实际是 HTML/JS:

攻击者上传:evil.png(内容为 <script>document.cookie...</script>)
服务器返回:Content-Type: image/png(按扩展名设置)
浏览器嗅探:发现内容是 HTML
浏览器渲染:作为 HTML 渲染,执行恶意 JS

这就是为什么用户上传的内容必须设置 X-Content-Type-Options: nosniff

3.3 嗅探的两种模式

模式触发条件行为
nosniff 模式服务器返回 X-Content-Type-Options: nosniff严格遵守 Content-Type,不嗅探
默认嗅探nosniff浏览器嗅探内容前几个字节

四、X-Content-Type-Options: nosniff 实战

4.1 何时必须设置 nosniff

所有用户上传内容必须设置 nosniff

  • 用户头像、附件上传的 CDN 域名
  • 第三方图片展示页面
  • UGC 富文本中的图片
  • 文件预览服务

4.2 Nginx 配置

# 全站启用
server {
    add_header X-Content-Type-Options nosniff always;
}

# 仅静态资源目录启用
location /uploads/ {
    add_header X-Content-Type-Options nosniff always;
    # 强制按扩展名返回正确 MIME,禁止 application/octet-stream
    types {
        image/jpeg jpg jpeg;
        image/png png;
        image/webp webp;
        image/gif gif;
        application/pdf pdf;
    }
    default_type application/octet-stream;
}

4.3 Apache 配置

# 全站启用
Header always set X-Content-Type-Options "nosniff"

# 仅特定目录启用
<Directory "/var/www/uploads">
    Header always set X-Content-Type-Options "nosniff"
</Directory>

4.4 验证 nosniff 生效

浏览器开发者工具 → Network → 点击响应 → Response Headers,应看到:

HTTP/1.1 200 OK
Content-Type: image/png
X-Content-Type-Options: nosniff

设置后即使文件内容是 HTML,浏览器也会按 image/png 处理,不会执行 JS。

五、Content-Disposition 与下载行为

5.1 inline vs attachment

Content-Disposition 控制「内联显示还是下载」:

# 内联显示(默认)
Content-Disposition: inline
Content-Type: image/png
──> 浏览器内联显示图片

# 触发下载
Content-Disposition: attachment
Content-Type: image/png
──> 浏览器触发下载

# 下载并指定文件名
Content-Disposition: attachment; filename="photo.png"
Content-Type: image/png
──> 下载为 photo.png

5.2 文件名编码(含中文)

非 ASCII 文件名(如中文)需用 filename* 与 RFC 5987 编码:

Content-Disposition: attachment; filename="download.zip"; filename*=UTF-8''%E4%B8%8B%E8%BD%BD.zip
  • filename:ASCII 兼容名(fallback)
  • filename*:UTF-8 编码的真实文件名(现代浏览器优先使用)

5.3 实战场景

# /downloads/ 路径下所有文件触发下载
location /downloads/ {
    add_header Content-Disposition "attachment";
}

# 根据请求参数动态设置文件名
location ~ ^/download/(.+)$ {
    add_header Content-Disposition "attachment; filename*=UTF-8''$1";
    # 注意 URL 编码
}

六、文件签名 magic number 校验实战

仅靠扩展名校验不安全,攻击者可改扩展名绕过 accept=".jpg"生产场景必须校验文件签名(magic number)

6.1 常见文件签名表

扩展名文件签名(前 N 字节,十六进制)MIME
PNG89 50 4E 47 0D 0A 1A 0A(8 字节)image/png
JPEGFF D8 FF(3 字节)image/jpeg
GIF47 49 46 38 37 6147 49 46 38 39 61(6 字节)image/gif
WebP52 49 46 46 ?? ?? ?? ?? 57 45 42 50(12 字节,RIFF + WEBP)image/webp
PDF25 50 44 46 2D(5 字节,%PDF-application/pdf
ZIP50 4B 03 04(4 字节,PK\x03\x04application/zip
GZIP1F 8B(2 字节)application/gzip
BMP42 4D(2 字节,BMimage/bmp
ICO00 00 01 00(4 字节)image/x-icon

6.2 前端 magic number 校验代码

// 通过文件头校验图片真实类型
async function checkImageSignature(file) {
  const buffer = await file.slice(0, 12).arrayBuffer();
  const bytes = new Uint8Array(buffer);
  
  // PNG: 89 50 4E 47 0D 0A 1A 0A
  if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47) {
    return 'image/png';
  }
  // JPEG: FF D8 FF
  if (bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[2] === 0xFF) {
    return 'image/jpeg';
  }
  // GIF: 47 49 46 38
  if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x38) {
    return 'image/gif';
  }
  // WebP: 52 49 46 46 ?? ?? ?? ?? 57 45 42 50
  if (bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x46 &&
      bytes[8] === 0x57 && bytes[9] === 0x45 && bytes[10] === 0x42 && bytes[11] === 0x50) {
    return 'image/webp';
  }
  return null; // 未知类型
}

// 使用:上传前校验
input.addEventListener('change', async (e) => {
  const file = e.target.files[0];
  const realType = await checkImageSignature(file);
  if (!realType || !realType.startsWith('image/')) {
    alert('文件签名不是图片');
    e.target.value = '';
    return;
  }
  // 校验通过
});

注意:前端校验仅为体验优化,不能作为安全边界,后端必须再次校验。

七、现代图片格式 MIME 与浏览器支持

7.1 主流图片格式 MIME 与兼容性

格式MIME文件签名ChromeFirefoxSafariEdge兼容性总评
JPEGimage/jpegFF D8 FF全平台
PNGimage/png89 50 4E 47...全平台
GIFimage/gif47 49 46 38...全平台
WebPimage/webp52 49 46 46...57 45 42 5014+现代浏览器
AVIFimage/avif00 00 00 20 66 74 79 70 61 76 69 6685+93+16+92+现代浏览器
HEICimage/heic见下文✅(macOS/iOS)仅 Apple 平台
SVGimage/svg+xml文本 <?xml / <svg全平台

7.2 AVIF 文件签名详解

AVIF 基于 ISOBMFF(ISO Base Media File Format),文件头是 ftyp box:

00 00 00 20 66 74 79 70 61 76 69 66
│         │  │  │  │  │  │  │  │  │
│         │  │  │  │  │  │  │  │  └─ 'f' (0x66)
│         │  │  │  │  │  │  │  └──── 'i' (0x69)
│         │  │  │  │  │  │  └─────── 'v' (0x76)
│         │  │  │  │  │  └────────── 'a' (0x61)
│         │  │  │  │  └───────────── 'f' (0x66)
│         │  │  │  └──────────────── 'p' (0x70)
│         │  │  └─────────────────── 't' (0x74)
│         │  └────────────────────── 'y' (0x79)
│         └───────────────────────── 't' (0x74)
└───────────────────────────────────── box size (0x20 = 32 bytes)

校验代码:

if (bytes[4] === 0x66 && bytes[5] === 0x74 && bytes[6] === 0x79 && bytes[7] === 0x70 &&
    bytes[8] === 0x61 && bytes[9] === 0x76 && bytes[10] === 0x69 && bytes[11] === 0x66) {
  return 'image/avif';
}

7.3 HEIC 的特殊性

HEIC 是 Apple 的图片格式(iOS 11+ 默认拍照格式),浏览器支持极少:

  • ✅ Safari(macOS 13+ / iOS 11+)
  • ❌ Chrome / Firefox / Edge

生产实践

<!-- 用 picture 元素提供 fallback -->
<picture>
  <source srcset="photo.avif" type="image/avif">
  <source srcset="photo.webp" type="image/webp">
  <source srcset="photo.jpg" type="image/jpeg">
  <img src="photo.jpg" alt="photo">
</picture>

7.4 服务端按 Accept 头自动协商

现代 CDN 与服务器支持按 Accept 头返回不同格式:

# Nginx 按 Accept 协商图片格式
map $http_accept $webp_suffix {
    default   "";
    "~*webp"  ".webp";
}

map $http_accept $avif_suffix {
    default   "";
    "~*avif"  ".avif";
}

server {
    location ~ ^/(.+)\.(jpg|jpeg|png)$ {
        # 优先尝试 AVIF,其次 WebP,最后原始格式
        try_files /$1$avif_suffix /$1$webp_suffix /$1.$2 =404;
    }
}

八、字体 MIME 与跨域

8.1 字体 MIME 类型

扩展名MIME浏览器
.woff2font/woff2现代浏览器
.wofffont/woffIE 9+ 与所有现代
.ttffont/ttf全平台
.otffont/otf全平台
.eotapplication/vnd.ms-fontobject仅 IE

8.2 字体跨域(CORS)

字体文件受同源策略保护,跨域加载需配置 CORS:

# 字体跨域配置
location ~* \.(woff2?|ttf|otf|eot)$ {
    add_header Access-Control-Allow-Origin "*";
    add_header Access-Control-Allow-Methods "GET";
    expires 30d;
}

⚠️ 注意:Access-Control-Allow-Origin: * 在某些场景下与 credentials: true 不兼容,需按实际配置具体来源。

8.3 字体预加载

字体文件加载耗时较长,建议预加载:

<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>

注意 crossorigin 属性必填,否则字体加载会失败。

九、Nginx / Apache MIME 配置实战

9.1 Nginx 配置完整示例

http {
    # 包含标准 MIME 映射
    include /etc/nginx/mime.types;
    
    # 默认类型(未知扩展名时)
    default_type application/octet-stream;
    
    # 安全:所有响应添加 nosniff
    add_header X-Content-Type-Options nosniff always;
    
    server {
        listen 80;
        server_name example.com;
        
        # 静态资源
        location /static/ {
            expires 30d;
            add_header Cache-Control "public, immutable";
        }
        
        # 用户上传内容:严格安全策略
        location /uploads/ {
            # 限制上传目录 MIME 白名单
            types {
                image/jpeg jpg jpeg;
                image/png png;
                image/webp webp;
                image/gif gif;
                application/pdf pdf;
            }
            default_type application/octet-stream;
            
            # 强制 nosniff
            add_header X-Content-Type-Options nosniff always;
            add_header Content-Disposition "inline";
            
            # 禁止执行脚本
            location ~ \.(php|jsp|asp|sh)$ {
                deny all;
            }
        }
        
        # JSON API
        location /api/ {
            default_type application/json;
            add_header X-Content-Type-Options nosniff always;
        }
    }
}

9.2 Apache 配置完整示例

# 全站启用 nosniff
Header always set X-Content-Type-Options "nosniff"

# 上传目录严格限制
<Directory "/var/www/uploads">
    # 强制按 MIME 白名单处理
    ForceType application/octet-stream
    
    <FilesMatch "\.(jpg|jpeg|png|gif|webp)$">
        ForceType image/jpeg
        Header set Content-Disposition "inline"
    </FilesMatch>
    
    # 禁止执行脚本
    <FilesMatch "\.(php|jsp|asp|sh|cgi)$">
        Require all denied
    </FilesMatch>
</Directory>

# JSON API
<Directory "/var/www/api">
    ForceType application/json
</Directory>

十、与工具矩阵的协同

MIME 类型查询与多个工具有协同价值:

协同场景工具组合工作流
HTTP 请求调试HTTP Headers 解析 + MIME 查询抓包分析 Content-Type 头时查询对应 MIME 含义
状态码与文件类型HTTP 状态码 + MIME 查询415 Unsupported Media Type 错误时查正确 MIME
请求代码生成HTTP 请求生成器 + MIME 查询生成多语言客户端代码时需正确设置 Content-Type
User-Agent 与兼容性User-Agent 解析 + MIME 查询根据 UA 判断支持的图片格式(如 Safari 支持 HEIC)
CSV 与 Markdown 互转CSV Markdown 转换 + MIME 查询表格文件类型识别与转换
图片处理图片压缩 + 图片转换 + MIME 查询不同格式的 MIME 与文件签名
DNS 与 TLSDNS 查询 + TLS 解析 + MIME 查询CDN 配置时按 MIME 协商与缓存策略

十一、最佳实践清单

  1. 所有响应必须设置正确的 Content-Type:不要依赖浏览器嗅探
  2. 用户上传内容必须设置 X-Content-Type-Options: nosniff:防 XSS 关键防线
  3. 上传目录限制 MIME 白名单:仅允许已知安全类型
  4. 校验文件签名而非扩展名:前端 + 后端双重 magic number 校验
  5. 现代图片格式提供 fallback<picture> 标签 + 多格式源
  6. 服务端按 Accept 头协商:自动返回 WebP/AVIF
  7. 字体跨域配置 CORSAccess-Control-Allow-Origin 必填
  8. 字体预加载用 crossorigin:避免加载失败
  9. JSON API 强制 application/json:避免被嗅探为 HTML
  10. 下载文件用 Content-Disposition: attachment:避免浏览器渲染

总结

MIME 类型不是「设置一次就忘」的细节,而是 Web 安全与体验的关键基础设施。理解三个核心要点:

  1. 浏览器以 Content-Type 为准(HTTP 传输时),但会嗅探内容(除非 nosniff
  2. 用户上传内容必须 nosniff:防 XSS 攻击的关键防线
  3. 现代格式需 fallback:AVIF/WebP/HEIC 浏览器支持不一,必须用 <picture> 兜底

下一步动手实操:访问 MIME 类型查询工具,按类别筛选或搜索目标扩展名,一键复制 MIME 类型用于 Nginx/Apache 配置或前端 accept 属性。