Django+Gunicorn+Nginx环境下CSRF验证失败及大文件上传求助
我之前调试类似的Django大文件上传场景时,也碰到过小文件正常、大文件触发CSRF验证失败的情况。结合你已经配置了CSRF中间件和表单token的前提,大概率是大文件上传过程中请求缓冲区不足、Cookie传递异常或者请求解析中断导致的,给你几个针对性的排查和解决步骤:
1. 先看Django的CSRF详细日志,定位具体失败原因
别瞎猜,先通过日志明确问题根源。在settings.py里添加CSRF相关的日志配置:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.security.csrf': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': False, }, }, }
重启服务后上传大文件,查看控制台日志:
- 如果显示
CSRF cookie not set:说明Cookie没传到Django,大概率是Nginx或浏览器的问题 - 如果显示
CSRF token missing or incorrect:可能是表单token和Cookie里的token不匹配,或者请求体解析异常
2. 调整Nginx的请求缓冲区配置(关键!)
大文件上传时,Nginx默认的缓冲区可能不够用,导致请求头(包括CSRF Cookie)无法完整传递给Gunicorn。在你的Nginx server配置块里添加以下参数:
# 增大请求缓冲区,确保CSRF Cookie和请求头能完整传递 proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; # 再次确认文件大小限制(确保配置生效) client_max_body_size 100M; # 改成你需要的最大文件大小
修改后重启Nginx:sudo systemctl restart nginx
3. 检查AJAX上传的CSRF头(如果用前端异步上传)
如果你是用JavaScript(比如AJAX、Fetch)上传大文件,别只依赖表单里的csrf_token字段,一定要在请求头里主动带上X-CSRFToken:
// 获取页面里的CSRF token const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value; const formData = new FormData(); formData.append('file', document.getElementById('file-input').files[0]); // 发起上传请求 fetch('/your-upload-endpoint/', { method: 'POST', headers: { 'X-CSRFToken': csrfToken, // 不要手动设置Content-Type!FormData会自动处理multipart/form-data }, body: formData });
注意:手动设置Content-Type会破坏FormData的边界符,导致Django无法正确解析请求体,进而拿不到CSRF token。
4. 调整Django的CSRF Cookie配置
如果你的网站用HTTPS,确保CSRF Cookie的安全设置正确,避免大文件上传时Cookie丢失:
# settings.py CSRF_COOKIE_SECURE = True # 仅在HTTPS下传递Cookie,适合生产环境 CSRF_COOKIE_HTTPONLY = False # 如果用前端JS上传,必须设为False,否则JS拿不到Cookie CSRF_COOKIE_SAMESITE = 'Lax' # 同域上传用Lax即可,跨域的话改成None(需配合CSRF_COOKIE_SECURE=True)
5. 优化Gunicorn的Worker配置
大文件上传是长连接操作,如果用Gunicorn默认的sync worker,可能会因为连接阻塞导致请求中断,进而触发CSRF验证失败。建议换成异步worker,比如gevent:
# 先安装gevent pip install gevent # 启动Gunicorn时指定worker类 gunicorn --worker-class=gevent --workers=4 --timeout=120 your_project.wsgi:application
异步worker能更好地处理长连接,避免请求中途断开。
6. 最后验证表单的CSRF Token位置
虽然理论上位置不影响,但可以试试把{% csrf_token %}放在<form>标签内的第一行,确保浏览器优先解析这个字段:
<form method="POST" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="file"> <button type="submit">上传</button> </form>
按这个顺序排查,基本能解决大文件上传的CSRF问题。先看日志定位原因,再针对性调整配置,效率会更高。
内容的提问来源于stack exchange,提问作者Iggdrazil

