PHP上传音频本身不涉及缓存策略,缓存由Web服务器(Nginx/Apache)或CDN对音频URL响应时控制;需配置静态文件缓存头,而非在upload.php中设置header。

PHP上传音频文件本身不涉及“缓存策略”设置——缓存发生在后续的播放、代理或分发环节,而非上传动作本身。 上传只是把客户端文件存到服务器磁盘,真正的缓存控制在你之后如何提供这个音频给前端播放时才起作用。
为什么 upload.php 里加 header("Cache-Control: ...") 没用
用户上传音频(比如通过 $_FILES)后,PHP 脚本执行结束,HTTP 响应已返回成功状态(如 200 或 302)。此时你对响应头做的任何设置(header())都只影响上传完成页的 HTML 响应,跟音频文件本身无关。音频文件是静态资源,它的缓存由 Web 服务器(Nginx/Apache)或后续的播放接口决定,不是上传脚本管的。
- 上传成功后,前端拿到的是音频的 URL(如
/uploads/123456.mp3),浏览器请求这个 URL 时才触发缓存逻辑 - 你在上传脚本里写
header("Cache-Control: public, max-age=31536000"),对/uploads/xxx.mp3的响应完全不起作用 - 真正要控制缓存,得让 Web 服务器对
.mp3、.m4a等后缀返回正确的Cache-Control和Expires头
Apache 下如何让上传的音频文件被强缓存
确保上传目录(如 /uploads/)下的音频文件由 Apache 直接服务,并启用 mod_expires。在项目根目录或虚拟主机配置中添加:
<IfModule mod_expires.c>
<FilesMatch "\.(mp3|m4a|ogg|wav)$">
ExpiresActive On
ExpiresDefault "access plus 1 year"
Header set Cache-Control "public, immutable, max-age=31536000"
</FilesMatch>
</IfModule>
- 必须确认
mod_headers和mod_expires已启用,否则Header set会报 500 错误 - 不要把音频上传到 Web 不可访问的路径(如
/var/www/private/audio/)后再用 PHP 代理输出——那样你就得自己控制header(),且容易内存溢出 - 如果用了 CDN,CDN 缓存行为优先级高于 Apache,需单独配置 CDN 的缓存规则
Nginx 下对上传音频启用 long-term 缓存
在 location 块中匹配音频后缀,直接返回静态文件并设置头:
立即学习“PHP免费学习笔记(深入)”;
location ~* \.(mp3|m4a|ogg|wav)$ {
expires 1y;
add_header Cache-Control "public, immutable, max-age=31536000";
add_header Accept-Ranges bytes;
}
-
expires 1y会自动设置Expires和Cache-Control,但显式写add_header更可控 -
immutable是关键:告诉浏览器该资源不会更新,避免在每次请求时发If-None-Match校验 - 务必确认上传文件权限为
644、目录为755,且 Nginx 用户(如www-data)有读取权限,否则返回 403
如果必须用 PHP 代理输出音频(比如鉴权后播放)
这时缓存控制落在 PHP 脚本上,但要注意:不能用 file_get_contents() + echo,必须流式输出:
if (file_exists($audio_path)) {
ob_end_clean();
header('Content-Type: audio/mpeg');
header('Content-Length: ' . filesize($audio_path));
header('Cache-Control: public, max-age=31536000');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT');
readfile($audio_path);
exit;
}
- 漏掉
ob_end_clean()或前面有空格/echo,会导致 HTTP 头发送失败,返回空白或 500 -
readfile()是唯一安全方式;echo file_get_contents()在 20MB 音频上极易触发Allowed memory size exhausted - 若需支持断点续播(如微信内嵌播放器),必须加
Accept-Ranges: bytes和正确处理Range请求头——这已超出基础缓存范畴,需额外实现
真正容易被忽略的是:上传后的文件路径是否暴露在 Web 可访问范围内、Web 服务器是否实际生效了缓存头、以及前端播放器(尤其是微信 WebView)对 immutable 的兼容性并不统一——有些版本会忽略它,仍走协商缓存。别只测 Chrome,务必用真机抓包验证响应头。



















