React调用Flask接口时HTTPS被重定向为HTTP的问题排查
问题概述
部署在Heroku上的React应用调用Flask后端/register接口时,出现两种异常情况:
- 添加强制HTTPS的重定向代码后,触发
GET https://example.com/register net::ERR_TOO_MANY_REDIRECTS无限重定向循环 - 移除重定向代码后,出现混合内容错误:
Mixed Content: The page at 'https://herokuapp.org/dashboard' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://example.com/register/'. This request has been blocked; the content must be served over HTTPS.
备注:代码中并未直接调用HTTP地址,HTTP请求来自重定向逻辑。
已尝试的代码
@app.before_request def before_request(): # avoid preflight redirects if request.method == 'OPTIONS': return jsonify({"status": "success"}), 200 # if not preflight request and not HTTPS redirect to HTTPS HTTPS_ENDPOINTS = ['/register'] if request.path in HTTPS_ENDPOINTS: if request.headers.get('X-Forwarded-Proto') == 'http': url = request.url.replace('http://', 'https://', 1) code = 301 return redirect(url, code=code)
问题原因
- Heroku反向代理识别问题:Heroku的请求会先经过平台的反向代理,实际协议通过
X-Forwarded-Proto头传递,但默认Flask不会信任这个头,导致即使实际是HTTPS请求,代码仍判断为HTTP,触发无限重定向。 - 混合内容根源:前端HTTPS页面发起的请求被错误重定向到HTTP地址,进而触发浏览器的混合内容拦截。
解决方案
1. 配置Flask信任Heroku反向代理
Flask需要明确知道自己运行在反向代理后方,才能正确读取代理传递的协议头。在app.py中添加以下配置:
from flask import Flask import os from werkzeug.middleware.proxy_fix import ProxyFix app = Flask(__name__) # 生产环境下配置代理信任 if os.environ.get('HEROKU'): # 信任X-Forwarded-Proto(协议)和X-Forwarded-Host(主机)头,参数对应代理层数 app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1) # 设置默认URL协议为HTTPS app.config['PREFERRED_URL_SCHEME'] = 'https'
说明:ProxyFix会让Flask的
request.is_secure、request.url等属性基于实际的请求协议(HTTPS),而非代理转发后的HTTP。
2. 简化重定向逻辑(或移除)
配置ProxyFix后,无需手动读取X-Forwarded-Proto头,可改用Flask内置的request.is_secure判断是否需要重定向:
@app.before_request def before_request(): if request.method == 'OPTIONS': return jsonify({"status": "success"}), 200 HTTPS_ENDPOINTS = ['/register'] if request.path in HTTPS_ENDPOINTS and not request.is_secure: url = request.url.replace('http://', 'https://', 1) # 临时用302避免浏览器缓存,确认正常后再改回301 return redirect(url, code=302)
如果配置ProxyFix后,所有请求已自动使用HTTPS,可直接移除这段重定向代码。
3. 验证前端API基础URL
确保React生产环境的API基础URL为HTTPS:
// React中配置API基础地址 const API_BASE_URL = process.env.NODE_ENV === 'production' ? 'https://your-heroku-app-name.herokuapp.com' : 'http://localhost:5000';
即使代码中没显式写HTTP,环境变量配置错误也可能导致生产环境请求HTTP地址。
4. 清除浏览器缓存
之前的301重定向可能被浏览器永久缓存,修复代码后需清除浏览器缓存(或使用隐私窗口测试),避免旧的重定向规则干扰。
内容的提问来源于stack exchange,提问作者beachCode

