烧瓶 url_for 生成 http URL 而不是 https

Posted

技术标签:

【中文标题】烧瓶 url_for 生成 http URL 而不是 https【英文标题】:Flask url_for generating http URL instead of https 【发布时间】:2013-01-26 11:24:22 【问题描述】:

当用户退出时,我使用url_for 生成重定向 URL:

return redirect(url_for('.index', _external=True))

但是,当我将页面更改为 https 连接时,url_for 仍然给我 http

我想明确要求url_for 在 URL 的开头添加 https

你能告诉我如何改变它吗?我查看了 Flask 文档,没有运气。

【问题讨论】:

你的烧瓶应用是如何部署的。因为https通常由wsgi handler处理 @JakobBowyer 我正在使用包含 Flask 的默认测试部署环境。只需调用python index.py。这就是 Flask 的 wsgi 处理程序。还要检查@leon 的答案。 这些解决方案都不起作用。因此不得不求助于将重定向 url 添加为配置条目。 【参考方案1】:

使用 Flask 0.10,将有一个比包装 url_for 更好的解决方案。如果您查看https://github.com/mitsuhiko/flask/commit/b5069d07a24a3c3a54fb056aa6f4076a0e7088c7,则添加了_scheme 参数。这意味着您可以执行以下操作:

url_for('secure_thingy',
        _external=True,
        _scheme='https',
        viewarg1=1, ...)

_scheme 设置 URL 方案,生成类似 https://.. 的 URL,而不是 http://。但是,默认情况下 Flask 只生成路径(没有主机或方案),因此您需要包含 _external=True 才能从 /secure_thingy 转到 https://example.com/secure_thingy


但是,请考虑将您的网站改为仅使用 HTTPS。 您似乎正在尝试仅对少数“安全”路由强制执行 HTTPS,但您无法确保您的 https - 如果链接到安全页面的页面未加密,则不会更改 URL。这类似于mixed content。

【讨论】:

奇怪的是,他们将_scheme 设为私有参数。至少它有效 @TheIncorrigible1 前缀为下划线,与路由参数不冲突。我不认为私有属性/方法的命名约定在这里适用。【参考方案2】:

如果您想影响所有服务器生成的 URL(url_forredirect)的 URL 方案,而不是在每次调用时都设置 _scheme,似乎“正确”的答案是使用WSGI 中间件,在这个 sn-p 中:http://flask.pocoo.org/snippets/35/

(This Flask bug 似乎确认这是首选方式。)

基本上,如果您的 WSGI 环境有environ['wsgi.url_scheme'] = 'https',那么url_for 将生成https: URL。

我从 url_for 获得了 http:// URL,因为我的服务器部署在 Elastic Beanstalk 负载均衡器后面,该负载均衡器以常规 HTTP 与服务器通信。我的解决方案(特定于 Elastic Beanstalk)是这样的(从上面链接的 sn-p 简化):

class ReverseProxied(object):
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        scheme = environ.get('HTTP_X_FORWARDED_PROTO')
        if scheme:
            environ['wsgi.url_scheme'] = scheme
        return self.app(environ, start_response)

app = Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)

其中特定于 Elastic Beanstalk 的部分是 HTTP_X_FORWARDED_PROTO。其他环境将有其他方法来确定外部 URL 是否包含 https。如果你只想一直使用HTTPS,你可以无条件设置environ['wsgi.url_scheme'] = 'https'

PREFERRED_URL_SCHEME 不是这样做的方法。我是ignored whenever a request is in progress。

【讨论】:

这个解决方案效果很好!。显然,这个问题专门发生在 aws elastic bean stalk 上。在没有弹性 beanstalk 的情况下托管相同的代码库时没有重定向问题。 非常感谢这个解决方案!在 Azure Web 服务上部署 Flask 应用程序时,这可以解决问题(至少使用 gunicorn 作为容器)。 感谢您的解决方案!对于像我这样的其他烧瓶菜鸟,我想补充一点,您可以“堆叠”或添加多个中间件。因此,如果您的代码已经有类似app.wsgi_app = ProxyFix(app.wsgi_app) 的行,您应该仍然可以添加 aldel 的解决方案而不会发生冲突。至少它对我有用。 这实际上发生在许多平台上,包括 Heroku。谢谢! 为通过 VPC 端点访问的私有 AWS API 网关实现了类似的功能。出于某种原因,标头 HTTP_X_FORWARDED_PROTO 没有发送到服务器,所以无论服务器发送什么,我都必须设置 environ['wsgi.url_scheme'] = 'https'(无论如何我都不想要 http)。【参考方案3】:

我尝试使用 url_for arg 接受的答案,但我发现使用 PREFERRED_URL_SCHEME 配置变量并将其设置为 https 更容易:

app.config.update(dict(
  PREFERRED_URL_SCHEME = 'https'
))

因为您不必将其添加到每个 url_for 调用中。

【讨论】:

对我来说最简单最简洁的答案【参考方案4】:

如果您通过 nginx 之类的反向代理访问您的网站,那么 Flask 会正确检测到 HTTP 的方案。

Browser -----HTTPS----> Reverse proxy -----HTTP----> Flask

最简单的解决方案是配置您的反向代理以设置X-Forwarded-Proto 标头。 Flask 会自动检测这个 header 并相应地管理方案。有一个more detailed explanation in the Flask documentation under the Proxy Setups section。例如,如果您使用 Nginx,则必须在 location 块中添加以下行。

proxy_set_header   X-Forwarded-Proto    $scheme;

正如其他提到的,如果您无法更改代理的配置,您可以使用 werkzeug ProxyFix 或按照文档中的说明构建自己的修复程序: http://flask.pocoo.org/docs/0.12/deploying/wsgi-standalone/#proxy-setups

【讨论】:

【参考方案5】:

在每个url_for() 调用上设置_scheme 非常繁琐,PREFERRED_URL_SCHEME 似乎不起作用。然而,在 WSGI 级别处理请求的假定方案似乎成功地说服了 Flask 始终构造 HTTPS URL:

def _force_https(app):
    def wrapper(environ, start_response):
        environ['wsgi.url_scheme'] = 'https'
        return app(environ, start_response)
    return wrapper

app = Flask(...)

app = _force_https(app)

【讨论】:

【参考方案6】:

对于最近在这里结束的任何人,都有一个官方的 uwsgi 修复程序: https://***.com/a/23504684/13777925

FWIW 这对我来说仍然不起作用,因为标头设置不正确,所以我增加了 ReversedProxied 中间件,如果这样发现更喜欢 https:

class ReverseProxied(object):
"""
Because we are reverse proxied from an aws load balancer
use environ/config to signal https
since flask ignores preferred_url_scheme in url_for calls
"""

    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        # if one of x_forwarded or preferred_url is https, prefer it.
        forwarded_scheme = environ.get("HTTP_X_FORWARDED_PROTO", None)
        preferred_scheme = app.config.get("PREFERRED_URL_SCHEME", None)
        if "https" in [forwarded_scheme, preferred_scheme]:
            environ["wsgi.url_scheme"] = "https"
        return self.app(environ, start_response)

称为:

app = flask.Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)

这样,如果您明确设置了环境变量“PREFERRED_URL_SCHEME”,或者如果 nginx/etc/proxy 设置了 X_FORWARDED_PROTO,它会做正确的事情。

【讨论】:

这个 shim 是让 Flask + OAuth2 在运行 gunicorn 的 Google Cloud Run 容器中正常工作所必需的。【参考方案7】:

我个人无法通过此处的任何答案解决此问题,但发现只需将--cert=adhoc 添加到烧瓶运行命令的末尾,这使得烧瓶应用程序使用 https 运行即可解决问题。

flask run --host=0.0.0.0 --cert=adhoc

【讨论】:

以上是关于烧瓶 url_for 生成 http URL 而不是 https的主要内容,如果未能解决你的问题,请参考以下文章

gunricorn + nginx 的烧瓶重定向(url_for)错误

带有多个参数的烧瓶 url_for()

记录一个url_for的用法

烧瓶-基础

Twitter oauth with flask_oauthlib,无法生成请求令牌

方法不允许烧瓶错误 405