博客音乐播放失效问题修复笔记
问题:本地/线上部署后刚部署的一段时间网易云音乐能播放,过一段时间(约几十分钟)就失效。 修复时间:2026-08-17
一、根因分析
网易云音频 CDN 直链的结构:
https://m701.music.126.net/20260817013332/xxxx/....mp3?vuutv=xxxx&cdntag=xxxx
vuutv= 是带时效的签名令牌,签发后约几十分钟即过期,过期后请求返回 403/404。
博客是纯静态站点,音乐链接的获取链条原本是:
- 构建期烘焙(
scripts/fetch-music.mjs):发布时解析 outer URL 拿到 CDN 直链,写进public/music/playlist.json—— 链接会随签名过期; - 运行时兜底(
music-engine.ts):播放失败时现场请求公共 meting 镜像换新链接 —— 但这些第三方镜像频繁宕机(实测 2026-08-16:api.injahow.cnRedis MISCONF 故障、api.mzzsf.com/api.wlai.vip超时)。
结果:刚部署时烘焙链接还新鲜 → 能播;几十分钟后签名过期 → 引擎找 meting 换链接 → 镜像宕机 → 播放失败。这就是"刚部署能用、过一段时间失效"的完整链条。
另外,outer URL 的 302 目标常为 http:// CDN 地址,浏览器端在 HTTPS 页面上直接请求会被混合内容策略拦截,所以客户端无法自行解决,必须有服务端环节。
二、修复方案:自建链接解析代理
在博客同服务器(117.72.175.4)上部署了一个零依赖的 Node 解析服务,nginx 反代到同源 /api/music/url:
GET /api/music/url?id=<歌曲ID> → { "id": "...", "url": "https://m701.music.126.net/... 新鲜直链" }
- 服务端手动跟随 outer URL 302 链(
redirect: manual,不下载音频 body); - http 直链统一改写为 https(绕开浏览器混合内容拦截);
- 域名白名单校验(
*.music.126.net/*.music.127.net); - 内存缓存 10 分钟,减少对网易云的请求。
服务器侧组件
| 组件 | 位置 |
|---|---|
| Node 运行时 (v16.20.2, npmmirror 静态包; CentOS7 glibc 2.17 最高只支持到官方 Node 16, 勿升 v18+) | /opt/node/ |
| 解析代理源码 (零依赖, 纯 node:https, 兼容 Node 16) | /opt/music-proxy/music-proxy.mjs |
| systemd 服务 | /etc/systemd/system/music-proxy.service(监听 127.0.0.1:8443) |
| nginx 反代片段 | /etc/nginx/conf.d/zhu-okol-music.inc(include 进 zhu-okol.conf 的 443 块) |
前端引擎自愈逻辑(music-engine.ts)
- 启动刷新:页面加载后,用自建代理把歌单里所有链接换新(与 meting 并行,谁成功用谁);
- 单曲失败换链重试:某首歌播放报错 → 先找自建代理换一条新链接重试同一首(不再直接跳下一首);
- 整单恢复:全部歌曲失败 → 自建代理整单换新 → 再不行才试公共 meting 镜像。
公共 meting 镜像降级为第三级兜底。
本地开发/预览也能用:代理响应带 Access-Control-Allow-Origin: *,引擎解析链为
同源 /api/music/url(生产)→ https://zhu-okol.cn/api/music/url(本地 404 时跨域回退)→ meting 镜像。
所以本地 开发模式.bat / 本地预览.bat 过期后同样能自愈。
部署自动化
deploy-tools/deploy.mjs 新增第 11 步:每次部署自动安装/更新 Node、上传代理源码、重启 systemd 服务、幂等更新 nginx 反代并验证公网解析。以后每次跑 Blog/部署到服务器.bat 都会自动保证代理在线。
只修复/更新代理而不重传站点:node deploy.mjs --proxy-only(跳过 70MB 站点上传,几秒完成)。
三、验证方法
# 服务器本机
curl -s http://127.0.0.1:8443/healthz # → ok
curl -s "http://127.0.0.1:8443/api/music/url?id=2098477275"
# 公网入口
curl -s "https://zhu-okol.cn/api/music/url?id=2098477275"
# → {"id":"2098477275","url":"https://m701.music.126.net/..."}
# 直链可用性 (应返回 206)
curl -s -o /dev/null -w "%{http_code}" -r 0-1023 "<上面拿到的直链>"
四、常见维护
systemctl status music-proxy # 服务状态
systemctl restart music-proxy # 重启
fns status # fast-note-sync (不相关, 别误动)
# 若某首歌解析返回 {"url":null} → 该歌下架/无版权, 去 siteConfig.ts 换歌
# 注意: cloudMusicIds 在 src/lib/siteConfig.ts 和 scripts/fetch-music.mjs 两处要同步改
五、备份与回滚
- nginx 原配置备份在服务器:
/etc/nginx/conf.d/zhu-okol.conf.bak - 回滚代理:
systemctl disable --now music-proxy,删除/etc/nginx/conf.d/zhu-okol-music.inc及 conf 里的 include 行,nginx -t && systemctl reload nginx