让 Nginx 代替 Django 直接传输文件:通过 X-Accel-Redirect 提升下载性能
大多数 Django 服务在提供受保护文件(仅登录用户可下载、付费后可查看等)时,都会使用 FileResponse 等方式,让 Python 进程读取文件 并将内容流式传输给客户端。若流量不大或仅在内部服务器间通信,这种方式足够好。
但当文件请求激增时,应用服务器(Python)会被文件传输任务占用,导致权限校验、业务逻辑、API 处理等核心工作变得困难。此时,典型的做法是让 Django 负责权限校验,而让 Nginx 负责文件传输,即使用 X-Accel-Redirect。
为什么直接让 Python 传输文件会成为瓶颈?
Django 直接传输文件的典型流程如下:
- 接收请求
- 校验权限
- 从磁盘/存储读取文件
- 通过应用进程将文件内容流式传输到网络
问题在于 第 3 步和第 4 步是“重量级”操作:
- 文件越大,传输时间越长
- 并发下载越多,工作进程/线程/进程会被占用
- 结果导致 API 响应延迟、超时,甚至需要扩容服务器
相比之下,Nginx 已经针对静态文件传输做了优化:内核级 sendfile、高效事件循环、缓冲/范围请求处理等功能,使其在文件交付方面表现更佳。
X-Accel-Redirect 的核心思路
Django 只做检查,Nginx 只做传输。
工作原理
- 客户端请求
/download/123 - Django 仅执行数据库查询和权限校验
- Django 在响应头中写入:
X-Accel-Redirect: /_protected/real/path/to/file.webp并返回空体 - Nginx 读取该头部,内部定位文件并直接将内容返回给客户端
这样,Django 完全不需要读取文件内容,所有文件传输工作都交给 Nginx。
何时特别适合使用 X-Accel-Redirect?
- 下载/图片请求量大且并发高:社区/聊天图片、附件、报告 PDF 下载等。
- 文件体积大或 Range 请求重要:视频、音频、大文件压缩包等。
- 想降低应用服务器成本:Python 进程昂贵,文件传输占用资源时会导致成本飙升。
何时可以不使用?
- 内部服务器间通信且流量低
- 文件请求少,API/DB 逻辑是瓶颈
- 文件存储在 S3 等外部对象存储,已通过 CDN 或预签名 URL 解决
在这些场景下,FileResponse 仍然足够。
实现示例:Django + Nginx

Nginx 配置示例
关键是 internal 指令。被标记为 internal 的 location 只能通过内部重定向访问,外部客户端无法直接访问。
# 真实文件的内部服务端点
location /_protected/ {
internal;
alias /var/app/protected_media/;
sendfile on;
tcp_nopush on;
# 可选:缓存/头部控制
# add_header Cache-Control "private, max-age=0";
}
- 假设
/var/app/protected_media/下存放实际文件 - 对外 URL 为
/download/...(由 Django 处理) - 内部传输路径统一为
/_protected/...
Django 视图示例
from django.http import HttpResponse, Http404
from django.contrib.auth.decorators import login_required
from django.utils.encoding import iri_to_uri
@login_required
def download(request, file_id):
# 1) 数据库查询 + 权限校验
obj = get_file_object_or_404(file_id)
if not obj.can_download(request.user):
raise Http404
# 2) 构造内部路径
internal_path = f"/_protected/{obj.storage_relpath}"
# 3) 只返回 X-Accel-Redirect 头部
response = HttpResponse()
response["X-Accel-Redirect"] = iri_to_uri(internal_path)
response["Content-Type"] = obj.content_type or "application/octet-stream"
response["Content-Disposition"] = f'attachment; filename="{obj.download_name}"'
return response
要点:
- 没有
FileResponse(open(...))之类的文件 I/O - 每个请求的处理时间极短,工作进程不会被文件传输占用
安全检查清单
- 内部路径必须由服务器决定:避免
/_protected/../../etc/passwd等路径穿越。 * 只使用数据库中安全的相对路径,或基于白名单映射。 - Nginx location 必须使用
internal:否则用户可以直接访问/_protected/...。 - 权限校验只在 Django 中完成:Nginx 只负责传输,安全性由 Django 保障。
通过第三方服务的替代方案
如果成本不是问题,也可以考虑直接从第三方存储提供文件:
- CDN 缓存:公开文件可在 Nginx 前使用 CDN 缓存,效果更佳。
- 预签名 URL(如 S3):对象存储可使用预签名 URL,省去 X-Accel-Redirect 的复杂性。
结语
总之,文件交付最好交给 Nginx 处理。Nginx 经过静态文件优化,能充分利用内核级别的 sendfile 等技术,从而显著提升性能。这样,应用服务器可以专注于权限校验和业务逻辑,即使下载流量激增,整体系统也能保持稳定。
如果流量不大,FileResponse 仍是干净且足够的选择。但当文件请求激增导致应用服务器崩溃时,X-Accel-Redirect 是最快、最有效的解决方案。
记住一句话:“权限由 Django 负责,传输由 Nginx 负责”。