必须在Flask初始化阶段设置MAX_CONTENT_LENGTH才有效;设了仍413是因Nginx(默认1MB)、uWSGI或Gunicorn在请求到达Flask前已拦截,需同步调大其限制;类型校验须读文件头而非仅看后缀或content_type。

必须在 Flask 初始化阶段设 MAX_CONTENT_LENGTH,否则大小限制无效;格式校验不能只看后缀或 content_type,得读文件头。
为什么设了 MAX_CONTENT_LENGTH 还是 413?
因为请求根本没到 Flask 层——Nginx、uWSGI 或 Gunicorn 在更早阶段就拦截了。
- Nginx 默认
client_max_body_size是 1MB,得手动改成和 Flask 一致,比如client_max_body_size 16M;,然后nginx -s reload - uWSGI 要加
--limit-post 16777216(单位字节) - Gunicorn 检查是否误配了
--limit-request-body,默认为 0 表示不限,别手抖填成 1024 - 这个配置必须写在
app = Flask(__name__)之后、任何@app.route之前,动态改(比如在视图里赋值)完全没用
allowed_file() 只检查后缀?不安全
用户能轻易伪造 filename="hack.php.jpg" 或把恶意 payload 改个后缀上传。仅靠 filename.rsplit('.', 1)[1] 或 file.content_type 都不可信。
- 真正有效的 MIME 校验要用
python-magic读文件头(magic bytes),安装:pip install python-magic - 调用方式:
magic.from_buffer(file.stream.read(2048), mime=True),记得file.stream.seek(0)回滚指针,否则后续save()会读空 - 不要用
imghdr或filetype:前者只支持有限图片类型,后者对 PDF、Office 文档等识别不准,2026 年实测误判率超 12%
多文件 + 大小 + 类型一起校验的典型写法
别在循环里反复调 file.stream.read(),避免重复读、内存暴涨;也别只检查 request.files 是否为空——超限后整个请求体被丢弃,request.files 和 request.form 都是空的。
- 先确认
'file' in request.files,再遍历request.files.getlist('file') - 每个文件先用
file.filename != ''排除空上传,再用allowed_file(file.filename)做初步后缀过滤(减轻 magic 开销) - 对通过后缀的文件,用
magic.from_buffer(..., mime=True)校验真实类型,白名单比对,如mime in {'image/jpeg', 'application/pdf'} - 注意:如果前端用了 Dropzone.js,它的
maxFilesize单位是 MiB(1024×1024 字节),不是 MB 或字节,maxFilesize: 100是约 102.4MB,别写成100 * 1024 * 1024再传过去
最易忽略的一点:MAX_CONTENT_LENGTH 限制的是整个请求体,包括所有文件 + 所有表单字段。如果允许用户在 request.form['desc'] 里贴一段 50MB 的 base64 图片,那即使单个文件很小,也会触发 413。所以光防大文件不够,还得在业务逻辑里做字段长度裁剪或拒绝高密度 payload。


















