必须同时设置filename和filename=utf-8''编码格式,filename提供ascii回退,filename用rawurlencode生成符合rfc 5987的utf-8编码,且两个单引号间不可有空格。

直接用 header('Content-Disposition: attachment; filename=中文.txt') 会导致 Chrome/Firefox 显示下划线或乱码,这不是 PHP 编码问题,而是 HTTP 头规范不兼容 —— 必须用 filename*=UTF-8'' 编码格式,且不能依赖浏览器自动解析。
Content-Disposition 的 filename 和 filename* 必须同时存在
只写 filename*=UTF-8''%E4%B8%AD%E6%96%87.txt,老版本 IE 或某些邮件客户端会忽略;只写 filename=中文.txt,现代浏览器直接拒识。正确做法是两者共存:
-
filename提供 ASCII 兼容 fallback(如filename="zhongwen.txt") -
filename*提供真实 UTF-8 编码(如filename*=UTF-8''%E4%B8%AD%E6%96%87.txt) - 注意
filename*中的两个单引号之间**不能有空格**,否则 RFC 5987 解析失败
PHP 生成 filename* 时必须用 rawurlencode,不能用 urlencode
urlencode() 会把空格转成 +,而 RFC 5987 要求用 %20;rawurlencode() 才符合标准:
$filename = '报告-2026年9月.xlsx';
$encoded = rawurlencode($filename);
header("Content-Disposition: attachment; filename=\"report.xlsx\"; filename*=UTF-8''{$encoded}");
漏掉 raw 前缀,或误用 mb_encode_mimeheader()(它输出的是 Q-encoded,非 RFC 5987 格式),都会导致中文显示为 _.xlsx。
文件系统路径中文没问题,但下载头和实际文件名要分开处理
服务器上保存文件用中文名完全可行(Linux/macOS 原生支持,Windows 需 mb_convert_encoding($path, 'GBK', 'UTF-8')),但下载响应头里的 filename 是纯 HTTP 行为,与磁盘路径无关:
- 不要对磁盘路径做
urlencode再塞进 header —— 那会生成非法文件名 - 不要从
$_SERVER['REQUEST_URI']或用户输入里直接取名 —— 可能含控制字符或路径穿越 - 推荐做法:服务端生成安全文件名(如
report_20260921_'.uniqid().'.xlsx),再用原始语义名仅用于filename*输出
最容易被忽略的是:Nginx/Apache 若启用了 gzip 压缩,可能缓存了旧的 Content-Disposition 头;上线后务必清空代理层和浏览器缓存,否则改对了代码也看不到效果。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











