根本原因是nginx未正确设置或透传content-disposition响应头,导致浏览器无法识别原始文件名而fallback至路径片段或默认名;需显式配置filename*扩展参数并确保alias路径斜杠一致、文件名utf-8编码对齐。

用 alias 代理文件时浏览器下载的文件名变成乱码、changeprotocol 或其他非预期名称,根本原因不是 alias 本身出错,而是 Nginx 没有正确设置或透传 Content-Disposition 响应头,导致浏览器无法识别原始文件名,只能 fallback 到路径最后一段或默认名。
Content-Disposition 头缺失或格式错误
当用户点击下载链接(如 /download/报告.pdf),Nginx 用 alias 找到真实文件后,若未显式设置 Content-Disposition,浏览器就按 URI 路径推导文件名:
→ 请求 /download/中国年报.pdf → Nginx 返回文件但无 filename= 参数 → 浏览器取 中国年报.pdf 这段作为名,但可能因编码不一致显示为 %E4%B8%AD%E5%9B%BD.pdf 或乱码。
更糟的是,某些配置下(如搭配 fastcgi 或 proxy_pass 错误混用),Nginx 可能完全不设该头,浏览器就用 location 匹配的路径片段(如 changeprotocol)当作默认名。
- 必须手动添加
add_header Content-Disposition 'attachment; filename="$uri"'; - 但
$uri是解码后的路径,含中文会出问题;推荐改用$request_uri并配合 URL 解码逻辑(需注意 Nginx 版本支持) - 更稳妥做法:用
map提取文件名并做 UTF-8 安全转义,再注入 header
alias 配置导致路径拼接错误,间接影响文件名解析
alias 的工作方式是“替换匹配前缀”,若 location 和 alias 路径末尾斜杠不一致,会导致实际读取路径错误,进而触发 Nginx 默认行为(如返回 404 后 fallback 到 index 或错误页),而错误页响应头里往往带 Content-Disposition: attachment; filename=changeprotocol —— 这就是那个诡异名字的来源。
- 错误写法:
location /dl { alias /data/files; }→ 请求/dl/测试.xlsx会被拼成/data/filestest.xlsx(漏斜杠,路径错位) - 正确写法:
location /dl/ { alias /data/files/; },确保两端都以/结尾 - 路径中含中文或空格时,alias 值必须用双引号包裹,且写原始字符(如
"/data/我的文件/"),不能 URL 编码
浏览器对 filename 参数的解码兼容性差
即使服务端设置了 Content-Disposition: attachment; filename="中文.pdf",Edge/IE 仍可能显示乱码——因为它们默认用 ISO-8859-1 解码 UTF-8 字符串。现代标准写法应使用 filename*=UTF-8''中文.pdf,但 Nginx 原生不支持动态生成该格式。
- 简单方案:在响应头中同时提供两个参数:
add_header Content-Disposition 'attachment; filename="report.pdf"; filename*=UTF-8\'\'%E4%B8%AD%E6%96%87.pdf'; - 复杂但可靠:用 Lua 模块(如 nginx-lua)或后端服务统一生成合规 header
- 客户端临时修复:Windows 组策略强制 Edge 使用 IE9 UA 字符串,触发兼容解码路径
与 root 或 proxy_pass 混用引发的隐性冲突
alias 不该和 root 共存于同一 location;更常见的是误将 alias 用于需动态处理的场景(如 phpMyAdmin),结果 PHP 文件被当作静态文件直接返回,响应头里没有 Content-Type: text/html,反而带上了下载头,浏览器就把它当二进制文件下载,文件名自然变成脚本名(如 index.php 或 changeprotocol)。
- phpMyAdmin 等 Web 应用必须走 fastcgi_pass,不能用 alias 直接暴露 PHP 文件
- 纯文件下载服务才适合 alias;动态内容请用 proxy_pass + 后端设置 header
- 检查 access.log 和 response header(用 curl -I),确认返回的是 200 + 正确 Content-Type,而非 200 + application/octet-stream











