Flask+Gunicorn+Nginx环境下自定义请求头无法接收问题
自定义请求头(如Api_key)无法被Flask服务接收的排查问题
我有一台配置Certbot SSL证书的VPS,采用Nginx+Gunicorn部署Flask应用,域名访问正常,但发送携带自定义请求头(如Api_key)的请求时,服务端无法接收这些头信息。已尝试配置Flask的CORS支持、在Nginx中添加underscores_in_headers指令,但问题仍未解决。
1. 服务端实际接收的请求头
DEBUG:root:Host: myweb.com X-Forwarded-Proto: https Connection: close Content-Type: application/json User-Agent: PostmanRuntime/7.37.3 Accept: */* Postman-Token: 27fd8606-d9ce-4120-9a68-c359b3fe0e60 Accept-Encoding: gzip, deflate, br
2. Flask应用代码(已配置CORS)
import subprocess from flask import Flask, jsonify, request, send_file import os from flask_cors import CORS from flask_cors import cross_origin import requests import logging logging.basicConfig(filename='flask_debug.log', level=logging.DEBUG) app = Flask(__name__) CORS(app, origins='*') @app.after_request def after_request(response): response.headers.add("Access-Control-Allow-Headers", "Content-Type, Api_key") return response def verify_api_key(api_key): response = requests.get( "https://myweb.com/api/verify-api-key", headers={'Api_key': api_key} ) if response.status_code == 200: result = response.json() return result.get("isValid", False) else: return False @app.route('/') def home(): return 'El servidor está en línea y funcionando !' @app.route("/api/get-user-videos", methods=["GET"]) def get_user_videos(): api_key = request.headers.get("Api_key") if not api_key: return jsonify({'error': 'Se requiere una API_KEY'}), 401 username = request.args.get("username") if not username: return jsonify({'error': 'Se requiere un nombre de usuario'}), 400 if not verify_api_key(api_key): return jsonify({'error': 'API_KEY no válida'}), 401 user_videos_folder = os.path.join("results", username) if not os.path.isdir(user_videos_folder): return jsonify({'error': 'Usuario no encontrado'}), 404 video_files = [ f for f in os.listdir(user_videos_folder) if f.endswith(".mp4") ] if not video_files: return jsonify({'error': 'Usuario sin videos'}), 404 return jsonify(video_files), 200 @app.route("/api/get-user-videos-list", methods=["GET"]) def get_user_videos_list(): api_key = request.headers.get("Api_key") logging.debug(request.headers) if not api_key: return jsonify({'error': 'Se requiere una API_KEY'}), 401 if not verify_api_key(api_key): return jsonify({'error': 'API_KEY no válida'}), 401 username = request.args.get("username") if not username: return jsonify({'error': 'Se requiere un nombre de usuario'}), 401 user_videos_folder = os.path.join("uploads", username) if not os.path.isdir(user_videos_folder): return jsonify({'error': 'Usuario no encontrado'}), 404 video_files = [ f for f in os.listdir(user_videos_folder) if f.endswith(".mp4") ] if not video_files: return jsonify({'error': 'Usuario sin videos'}), 404 alphabet = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ" missing_letters = [letter for letter in alphabet if letter not in ''.join(video_files)] if not missing_letters: return jsonify(video_files), 200 return jsonify({'error': 'Alfabeto incompleto !'}), 400 @app.route("/api/get-user-video/<username>/<video_name>", methods=["GET"]) def get_user_video(username, video_name): api_key = request.headers.get("Api_key") if not api_key: return jsonify({'error': 'Se requiere una API_KEY'}), 401 if not verify_api_key(api_key): return jsonify({'error': 'API_KEY no válida'}), 401 if not username: return jsonify({'error': 'Se requiere un username'}), 401 user_video_path = os.path.join("uploads", username, video_name) if not os.path.isfile(user_video_path): return jsonify({'error': 'Video no encontrado'}), 404 return send_file(user_video_path, as_attachment=True) if __name__ == '__main__': app.run(host='0.0.0.0', threaded=True)
3. Gunicorn服务配置文件
[Unit] Description=Server Videos After=network.target [Service] User=myuser Group=www-data WorkingDirectory=/home/videos/flask-server-videos Environment="PATH=/home/videos/env/videos/bin" ExecStart=/home/videos/env/videos/bin/gunicorn --workers 2 --bind unix:app.sock -m 007 wsgi:app Restart=always [Install] WantedBy=multi-user.target
4. Nginx配置文件(已添加underscores_in_headers指令尝试解决)
server { underscores_in_headers on; location / { include proxy_params; proxy_pass http://unix:/home/videos/flask-server-videos/app.sock; } listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/myweb.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/myweb.com/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot } server { if ($host = myweb.com) { return 301 https://$host$request_uri; } # managed by Certbot listen 80; server_name myweb.com; return 404; # managed by Certbot }
5. 测试请求示例
curl "https://myweb.com/api/get-user-videos-list?username=Jorge" ^ -H "Accept: */*" ^ -H "Accept-Language: es,en-US;q=0.9,en;q=0.8,es-MX;q=0.7" ^ -H "Api_key: abcdefgh123456789" ^ -H "Connection: keep-alive" ^ -H "Content-Type: application/json" ^ -H "Origin: https://anotherweb.com" ^ -H "Referer: https://myweb.com/" ^ -H "User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 Edg/124.0.0.0" ^ -H ^"sec-ch-ua: ^\^"Chromium^\^";v=^\^"124^\^", ^\^"Microsoft Edge^\^";v=^\^"124^\^", ^\^"Not-A.Brand^\^";v=^\^"99^\^"^" ^
排查建议
检查Nginx的
proxy_params文件
默认的proxy_params可能过滤带下划线的请求头,打开/etc/nginx/proxy_params查看,确保包含必要的转发配置,可手动添加自定义头转发:proxy_set_header Api_key $http_api_key;添加后重启Nginx:
sudo systemctl restart nginx修改请求头命名规范
遵循HTTP标准,将自定义头改为X-Api-Key格式,同时更新Flask代码中的获取逻辑:request.headers.get("X-Api-Key"),并同步更新CORS允许头配置。直接测试Gunicorn服务
临时修改Gunicorn配置绑定端口(如--bind 0.0.0.0:8000),绕过Nginx直接测试:curl "http://localhost:8000/api/get-user-videos-list?username=Jorge" -H "Api_key: abcdefgh123456789"若能正常接收,说明问题出在Nginx配置;反之则排查Flask中间件或请求头处理逻辑。
检查Nginx日志
在Nginx的server块中添加详细日志格式,记录自定义头:log_format debug_log '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_api_key"'; access_log /var/log/nginx/debug_access.log debug_log;重启Nginx后发起请求,查看日志确认请求头是否到达Nginx。
打印Flask原始请求头
在Flask代码中添加原始请求头打印逻辑,确认头信息是否被修改:from werkzeug.datastructures import EnvironHeaders logging.debug(dict(EnvironHeaders(request.environ)))查看日志输出,确认自定义头是否存在。
内容的提问来源于stack exchange,提问作者Jorge Zapien
相关产品推荐
相关产品推荐

