download 属性在 file:// 协议和 ios safari 中完全失效,跨域链接也无效;必须通过 http 服务、后端 content-disposition 响应头或 fetch+blob 方案实现下载。

download 属性在 file:// 协议下完全失效
双击 HTML 文件直接打开(URL 是 file:///xxx/index.html)时,download 属性一律被浏览器静默忽略——这不是 bug,是同源策略的硬性限制:file:// 协议不参与同源判定,所有资源都被视为跨源。
必须启动本地 HTTP 服务才能让 download 生效:
- Python 3 用户:在 HTML 所在目录运行
python3 -m http.server 8000,然后访问http://localhost:8000/ - Node.js 用户:用
npx http-server -p 8000或npx serve -p 8000 - VS Code 用户:安装 Live Server 插件,右键 HTML 文件选择 “Open with Live Server”
只要 URL 变成 http:// 或 https:// 开头,同源判断才开始工作,download 才可能起作用。
跨域链接加 download 会被浏览器直接忽略
给 <a href="https://cdn.example.com/report.pdf" download></a> 加 download 属性,Chrome、Edge、Firefox 都会跳过它,退化为新标签页打开或内联预览。这是安全机制,不是兼容性问题。
可行方案只有两个:
- 后端加响应头:
Content-Disposition: attachment; filename="report.pdf"——这是最稳的方式,无论是否跨域、是否用<a></a>标签,都会触发下载 - 前端 fetch + Blob:
fetch()请求资源 →response.blob()→URL.createObjectURL()→ 创建临时<a></a>并.click();注意服务端需返回Access-Control-Allow-Origin,且必须调用URL.revokeObjectURL()清理内存
别试图用 JS 动态改 href 或拼接 download 值绕过——无效,还可能引入 XSS 风险。
download 值含非法字符导致文件名被篡改
把完整 URL 当作 download 值,比如 download="/assets/file.pdf?1646327099",浏览器会把 ?、/、= 全替换成下划线,最终下载成 _assets_file.pdf_1646327099。
正确做法是职责分离:
-
href负责传完整地址(可带查询参数,用于缓存控制、签名等) -
download只传纯净文件名,例如download="report-2026Q3.pdf"或download="销售数据.xlsx" - 后端生成时用
pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_BASENAME)提取合法 basename
如果文件名含中文,现代浏览器基本支持,但旧版 Safari 可能乱码,生产环境建议用英文+下划线命名。
iOS Safari 全系不支持 download 属性
无论同源还是跨域,无论 <a></a> 还是 JS 模拟点击,iOS/iPadOS Safari 都不会弹出保存对话框——这是系统级限制,连 fetch + Blob + .click() 都被拦截。
目前没有前端纯方案能绕过:
- 服务端加
Content-Disposition头仍是唯一可靠方式(Safari 会尊重该响应头) - 若必须走前端,只能降级提示用户“长按链接 → 选择‘下载’”,并确保链接指向的是真实文件(如
.pdf、.xlsx),而非接口 - 不要依赖
target="_blank"或rel="noopener"来“修复”下载,它们和download冲突,反而干扰行为
真正容易被忽略的点是:你写的代码在 Chrome 和 Edge 里跑得飞起,一上真机就完全没反应——不是代码错了,是平台根本不认这个属性。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











