Safari和旧版Firefox无法播放音频的根本原因是浏览器未加载资源,需严格匹配type值、服务端MIME配置及用户手势触发。

直接写 <audio src="a.mp3"> 在 Safari(尤其 iOS)和旧版 Firefox 上基本播不出来,不是代码漏了,是浏览器根本没加载——它连请求都不发。
每个 <source> 必须带精确 type 值
浏览器不解析文件头,只按 type 字符串匹配解码器。省略 type 或写错,等于主动跳过该源。
-
type="audio/mp3"是无效值,必须写type="audio/mpeg" -
type="audio/ogg"在 Safari 17.5+ 会被忽略,必须写type="audio/ogg; codecs=opus"或type="audio/ogg; codecs=vorbis" -
type="audio/mp4"不够,得明确编码:type="audio/mp4; codecs=aac"(Safari 要求 AAC-LC,不认 HE-AAC) - 验证是否真支持:控制台运行
Audio.canPlayType('audio/ogg; codecs="opus"'),返回"probably"才算稳
<source> 的 DOM 顺序决定加载优先级
浏览器从上到下逐个调用 canPlayType(),第一个返回 "probably" 或 "maybe" 的就加载,后面全跳过。顺序错了,高兼容格式根本没机会执行。
- 推荐 fallback 顺序:
<source src="a.opus" type="audio/ogg; codecs=opus">→<source src="a.m4a" type="audio/mp4; codecs=aac">→<source src="a.mp3" type="audio/mpeg">→<source src="a.wav" type="audio/wav"> - 别把
.wav放第一位:16-bit PCM 文件体积大,Firefox 虽能解,但用户得等完整下载才出声 - 别用 JS 动态插入
<source>再调load():iOS Safari 对动态注入的 source 支持不稳定,静态写死更可靠
服务端 MIME 和 Range 配置比前端代码还关键
前端写成诗,服务端配错一行,音频照样静默失败。90% 的 “卡 loading”“拖不动进度条”“duration 为 NaN”,根源都在服务端。
立即学习“前端免费学习笔记(深入)”;
- Nginx 必须显式声明类型:
add_type audio/ogg .opus;、add_type audio/mp4 .m4a;、add_type audio/mpeg .mp3;、add_type audio/wav .wav; - 响应头
Content-Type必须和<source type>完全一致,比如audio/ogg; codecs=opus对应Content-Type: audio/ogg,返回application/octet-stream就直接失败 - 必须支持
Range请求,否则 Safari/iOS 无法分片加载,currentTime设置无效、拖动失效 - 跨域音频必须带
Access-Control-Allow-Origin: *,否则 Safari 静默拒绝解码,连error事件都不抛
JS 播放必须绑定真实用户手势,且捕获 Promise 拒绝
现代浏览器(含微信 iOS)强制要求首次播放由用户点击/触摸触发,否则 play() 返回被 reject 的 Promise,不是 JS 写错了,是策略拦截。
- 删掉
autoplay属性,改用按钮点击触发:button.addEventListener('click', () => audio.play().catch(e => {})) - iOS Safari 更严:元素必须在视口内、未被
display: none或opacity: 0遮挡,否则即使点过也静音 - 监听
error事件看具体失败原因:audio.addEventListener('error', () => console.log(audio.error?.code)),code === 4表示所有<source>均不可用 - 不要在
DOMContentLoaded或load回调里调play(),100% 失败
最易被忽略的是服务端配置——哪怕你 <source> 写得再全、顺序再对、JS 触发再规范,只要 Nginx 缺了 add_type 或没开 Access-Control-Allow-Origin,音频就是哑的,而且往往连错误提示都没有。



















