跨域文件下载时浏览器无法识别文件名的根本原因是响应头缺少 Access-Control-Expose-Headers: Content-Disposition 声明,导致 JS 无法读取该头;需后端规范设置 filename*(RFC 5987)格式的 Content-Disposition,并在 Nginx 中配置 add_header 'Access-Control-Expose-Headers' 'Content-Disposition' always; 同时带凭证时须动态设置 Access-Control-Allow-Origin 并启用 credentials。

跨域文件下载时浏览器无法正确识别文件名,根本原因不是 CORS 本身限制了文件名,而是响应头中缺少关键字段,导致浏览器无法从 Content-Disposition 中安全提取并应用文件名——尤其在跨域上下文中,浏览器对响应头的暴露有严格限制。
必须显式声明 Access-Control-Expose-Headers
浏览器默认只允许 JavaScript 读取少数“简单响应头”(如 Cache-Control、Content-Type),而 Content-Disposition 不在其中。若后端通过该头指定文件名(例如:Content-Disposition: attachment; filename="report.pdf"),前端 JS 调用 response.headers.get('Content-Disposition') 会返回 null,除非你明确告诉浏览器:“这个头可以被前端脚本访问”。
- 在 Nginx 的对应
location块中添加:
add_header 'Access-Control-Expose-Headers' 'Content-Disposition' always;
- 注意:必须加
always,否则 OPTIONS 预检响应和 204 响应中不会包含该头,导致后续真实请求也被视为不合规而拒绝暴露 - 如果还需支持其他自定义头(如
X-File-Size或X-Download-Name),可一并列出:'Content-Disposition, X-File-Size'
确保 Content-Disposition 由后端生成且格式规范
Nginx 不生成 Content-Disposition,它只是透传或补充响应头。该头必须由你的后端服务(如 Node.js、Spring Boot、Django)在返回文件流时设置,且推荐使用 RFC 5987 格式以兼容中文/特殊字符:
- ✅ 推荐写法(带编码):
Content-Disposition: attachment; filename*=UTF-8''%E6%8A%A5%E8%A1%A8.pdf - ⚠️ 避免仅用 ASCII 文件名:
filename="report.pdf"(对中文失效) - ❌ 不要让 Nginx 用
add_header硬写该头——它无法动态获取原始文件名,容易出错
带凭证下载需同步校验 Origin 白名单
如果前端下载请求携带 Cookie 或 Authorization(即 credentials: true),则 Access-Control-Allow-Origin 不能为 *,否则浏览器直接屏蔽整个响应(包括 Content-Disposition)。
- 采用
map动态匹配可信源(如前文所述),并确保add_header 'Access-Control-Allow-Origin' $cors_origin always; - 同时启用凭据支持:
add_header 'Access-Control-Allow-Credentials' 'true' always; - 这两者必须同时满足,否则即使暴露了
Content-Disposition,浏览器也不允许 JS 读取
前端触发下载时需主动解析 header
光有响应头还不够。前端要用 fetch + blob 方式手动构建下载链接,并从响应头中提取文件名:
- 示例关键逻辑(无框架):
const disposition = response.headers.get('Content-Disposition');
let filename = 'download.bin';
if (disposition && disposition.indexOf('filename=') !== -1) {
filename = decodeURIComponent(disposition.split('filename=')[1].split(';')[0].replace(/["']/g, ''));
}
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();


















