S3签名请求时XMLHttpRequest端点因非HTTPS被拦截问题排查
问题分析与解决方案
首先,你的核心问题是混合内容(Mixed Content)错误:页面通过HTTPS加载,但AJAX请求却发起了HTTP协议的请求,浏览器出于安全策略直接拦截了这个不安全的请求,导致签名接口调用失败。
为什么会出现这个问题?
虽然你在前端写的是相对路径/sign_s3,但浏览器却将其解析为http://www.example.com/sign_s3,而非继承页面的HTTPS协议。结合Heroku部署环境,主要有以下几个可能的根源:
可能的原因及对应解决方案
1. Flask应用未正确识别Heroku的反向代理协议头
Heroku的请求是通过反向代理转发的,它会通过X-Forwarded-Proto头传递原始请求的HTTPS协议,但Flask默认不会信任这个头,导致应用误以为请求是HTTP协议,进而影响URL解析。
解决方案:
在Flask应用中添加代理头处理配置,让应用正确识别HTTPS协议:
from flask import Flask from werkzeug.middleware.proxy_fix import ProxyFix app = Flask(__name__) # 信任反向代理传递的协议、主机头等信息 app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1) # 配置默认URL协议为HTTPS app.config['PREFERRED_URL_SCHEME'] = 'https' # 确保Cookie仅通过HTTPS传输(增强安全性) app.config['SESSION_COOKIE_SECURE'] = True app.config['REMEMBER_COOKIE_SECURE'] = True
2. HTML模板中存在HTTP协议的<base>标签
如果你的页面模板里设置了<base href="http://www.example.com/">,浏览器会基于这个HTTP地址解析所有相对路径,导致AJAX请求变成HTTP协议。
解决方案:
- 检查页面的HTML代码,移除或修改
<base>标签为HTTPS:<!-- 移除错误的base标签,或者修改为HTTPS --> <base href="https://www.example.com/">
3. 前端代码未显式指定协议(可选优化)
虽然相对路径理论上应该继承页面协议,但可以通过显式构造URL的方式避免解析错误:
function getSignedRequest(file) { // 显式使用当前页面的协议和主机构造URL const url = `${window.location.protocol}//${window.location.host}/sign_s3?file_name=${encodeURIComponent(file.name)}&file_type=${encodeURIComponent(file.type)}`; var xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log('got signed request'); var response = JSON.parse(xhr.responseText); console.log(response); console.log('uploadFile', response.url) uploadFile(file, response.data, response.url); } else { console.log("Could not get signed URL."); } } }; xhr.send(); }
同时注意用encodeURIComponent对文件名和类型编码,避免特殊字符导致的URL解析问题。
4. Heroku强制HTTPS跳转配置不完整
确保你已经启用了Heroku的强制HTTPS跳转:
# 安装SSL重定向插件 heroku plugins:install heroku-ssl-redirect # 开启强制HTTPS跳转 heroku config:set SSL_REDIRECT=true
另外,检查SSL证书状态是否正常:
heroku certs
确认证书状态为Trust,且覆盖了你的域名。
5. 浏览器缓存旧的HTTP资源
浏览器可能缓存了旧的HTTP版本页面或脚本,导致请求逻辑未更新。可以:
- 清除浏览器缓存(Ctrl+Shift+R强制刷新)
- 在静态资源文件名后添加版本号(比如
app.js?v=2),强制浏览器加载新资源
内容的提问来源于stack exchange,提问作者Roman
相关产品推荐
相关产品推荐

