Flask子域名转发脚本无法正确传递上传文件问题排查
问题解决:Flask子域名转发时文件上传与请求头保留兼容方案
问题根源
转发multipart/form-data类型的POST请求时,直接传递原请求的所有头会引发冲突:原请求的Content-Length和带boundary的Content-Type会被带到转发请求中,但requests库在处理files和data参数时会自动生成新的Content-Type(包含正确的boundary)和Content-Length,两者冲突导致目标Flask服务无法正确解析表单数据,最终request.files为空。
解决方案
复制原请求头后,移除会引发冲突的Content-Length和Content-Type,让requests库自动处理这两个头,同时保留其他关键头(如Cookie、Cf-Connecting-Ip、X-Forwarded-For等)。
修改后的转发代码
from flask import Flask, request, render_template, Response import requests app = Flask(__name__, template_folder='../templates') @app.route('/', defaults={'path': ''}, methods=['GET', 'POST']) @app.route('/<path:path>', methods=['GET', 'POST']) def index(path): headers = dict(request.headers) # 复制原请求头为可变字典 subdomain = headers['Host'].replace("mydomain.com", "") port_mapping = { '': 5001, 'test.': 5002 } if subdomain in port_mapping: new_port = port_mapping[subdomain] destination_url = f'http://127.0.0.1:{new_port}' if request.method == 'POST': # 移除冲突头,让requests自动生成正确的multipart头信息 headers.pop('Content-Length', None) headers.pop('Content-Type', None) response = requests.post( destination_url + request.full_path, headers=headers, data=request.form, files=request.files ) else: response = requests.get( destination_url + request.full_path, headers=headers ) return Response(response.content, status=response.status_code, headers=dict(response.headers)) else: return render_template("/errors/subdomain_not_found.html") if __name__ == '__main__': app.run(host='127.0.0.1', port=5000, debug=True)
关键修改说明
headers = dict(request.headers):将原请求头转换为可变字典,便于修改操作headers.pop('Content-Length', None)和headers.pop('Content-Type', None):安全移除会与requests自动生成的头冲突的字段,避免解析错误- 保留所有其他请求头,确保Session(Cookie)、客户端IP(Cf-Connecting-Ip、X-Forwarded-For)等关键信息正常传递
验证方式
- 通过子域名访问上传页面,上传文件后,目标服务(5002端口)的
request.files应能正确获取到文件对象 - 检查目标服务的
request.cookies是否包含原Session值,request.headers.get('Cf-Connecting-Ip')是否正确获取客户端IP
内容的提问来源于stack exchange,提问作者ward franssen
相关产品推荐
相关产品推荐

