应手动拦截origin头动态校验而非依赖flask-cors静态配置,通过@app.after_request和options路由统一处理跨域,严格匹配白名单、支持运行时更新、兼顾凭证与预检请求。

Flask 中直接写死多个域名会出问题
用 CORS(app, origins=["https://a.com", "https://b.net"]) 看似简单,但一旦新增域名就得改代码、重启服务,线上环境根本不可行。更关键的是,flask-cors 默认不支持运行时动态校验 Origin 头——它只在初始化时解析一次配置。
手动拦截并动态验证 Origin 头才是正解
绕过 flask-cors 的声明式配置,改用 Flask 的 @app.after_request + request.headers.get("Origin") 手动控制响应头。这样能实时读取每个请求的 Origin,做白名单匹配、协议/端口校验、甚至查数据库。
实操要点:
- 必须检查
Origin是否存在且非空,否则可能把Access-Control-Allow-Origin: *错发给非浏览器请求(如 curl) - 匹配时建议用严格前缀比对(如
origin.startswith("https://trusted-")),避免"https://evil.com.evil-site.com"这类伪造域名绕过 - 如果允许带凭证(
credentials=True),Access-Control-Allow-Origin不能为*,必须回传具体匹配到的Origin值 - 别忘了同步设置
Access-Control-Allow-Methods和Access-Control-Allow-Headers,否则预检请求(OPTIONS)会失败
处理 OPTIONS 预检请求要单独响应
浏览器在发复杂请求(如带 Authorization 或自定义 header)前,会先发一个 OPTIONS 请求。Flask 默认不处理它,会导致 405 错误。
正确做法是加一个通配路由捕获所有 OPTIONS:
@app.route('/<path>', methods=['OPTIONS'])
def handle_options(path):
resp = make_response()
resp.headers['Access-Control-Allow-Origin'] = request.headers.get('Origin', '')
resp.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,PATCH,OPTIONS'
resp.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization,X-Requested-With'
resp.headers['Access-Control-Allow-Credentials'] = 'true'
return resp
</path>
注意:这个路由必须放在所有其他路由注册之后,否则会覆盖业务接口。
生产环境别忽略 Referer 和协议一致性
仅靠 Origin 头不够安全。有些老客户端或代理可能不带该头,或者被篡改。可叠加校验:
- 检查
request.scheme是否为https(若要求强制 HTTPS) - 对比
request.headers.get("Referer")的 host 是否与Origin一致(虽不绝对可靠,但多一层过滤) - 把域名白名单存在 Redis 或配置中心,定期拉取更新,避免每次请求都查数据库
- 记录非法
Origin访问日志,用于后续安全审计
动态跨域真正的难点不在代码怎么写,而在判断逻辑是否覆盖了协议降级、子域泛匹配、IP 直连等边缘场景——这些往往上线后才暴露。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











