简体   繁体   English

Flask url_for 生成 http URL 而不是 https

[英]Flask url_for generating http URL instead of https

I am using url_for to generate a redirect URL when a user has logged out:当用户注销时,我使用url_for生成重定向 URL:

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

However, when I changed the page to a https connection, the url_for still gives me http .但是,当我将页面更改为https连接时, url_for仍然给我http

I would like to explicitly ask url_for to add https at the beginning of a URL.我想明确要求url_for在 URL 的开头添加https

Can you point me how to change it?你能指点我怎么改吗? I looked at Flask docs, without luck.我查看了 Flask 文档,但运气不佳。

With Flask 0.10, there will be a much better solution available than wrapping url_for .使用 Flask 0.10,将有一个比包装url_for更好的解决方案。 If you look at https://github.com/mitsuhiko/flask/commit/b5069d07a24a3c3a54fb056aa6f4076a0e7088c7 , a _scheme parameter has been added.如果您查看https://github.com/mitsuhiko/flask/commit/b5069d07a24a3c3a54fb056aa6f4076a0e7088c7_scheme添加一个_scheme参数。 Which means you can do the following:这意味着您可以执行以下操作:

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

_scheme sets the URL scheme, generating a URL like https://.. instead of http:// . _scheme设置 URL 方案,生成一个 URL,如https://..而不是http:// However, by default Flask only generates paths (without host or scheme), so you will need to include the _external=True to go from /secure_thingy to https://example.com/secure_thingy .但是,默认情况下 Flask 仅生成路径(没有主机或方案),因此您需要包含/secure_thingy _external=True以从/secure_thingy转到https://example.com/secure_thingy


However, consider making your website HTTPS-only instead.但是,请考虑让您的网站仅使用 HTTPS。 It seems that you're trying to partially enforce HTTPS for only a few "secure" routes, but you can't ensure that your https-URL is not changed if the page linking to the secure page is not encrypted.似乎您试图仅对少数“安全”路由部分强制执行 HTTPS,但如果链接到安全页面的页面未加密,则无法确保您的 https-URL 不会更改。 This is similar to mixed content .这类似于混合内容

If you want to affect the URL scheme for all server-generated URLs ( url_for and redirect ), rather than having to set _scheme on every call, it seems that the "correct" answer is to use WSGI middleware, as in this snippet: http://flask.pocoo.org/snippets/35/如果您想影响所有服务器生成的 URL( url_forredirect )的 URL 方案,而不必在每次调用时设置_scheme ,似乎“正确”的答案是使用 WSGI 中间件,如以下代码段所示: http ://flask.pocoo.org/snippets/35/

( This Flask bug seems to confirm that that is the preferred way.) 这个 Flask 错误似乎证实这是首选方式。)

Basically, if your WSGI environment has environ['wsgi.url_scheme'] = 'https' , then url_for will generate https: URLs.基本上,如果你的 WSGI 环境有 environment environ['wsgi.url_scheme'] = 'https' ,那么url_for将生成https: URL。

I was getting http:// URLs from url_for because my server was deployed behind an Elastic Beanstalk load balancer, which communicates with the server in regular HTTP.我从url_for获取http:// URL,因为我的服务器部署在 Elastic Beanstalk 负载均衡器后面,该负载均衡器以常规 HTTP 与服务器通信。 My solution (specific to Elastic Beanstalk) was like this (simplified from the snippet linked above):我的解决方案(特定于 Elastic Beanstalk)是这样的(从上面链接的片段中简化):

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)

The Elastic Beanstalk-specific part of that is HTTP_X_FORWARDED_PROTO .其中 Elastic Beanstalk 特定的部分是HTTP_X_FORWARDED_PROTO Other environments would have other ways of determining whether the external URL included https.其他环境有其他方法来确定外部 URL 是否包含 https。 If you just want to always use HTTPS, you could unconditionally set environ['wsgi.url_scheme'] = 'https' .如果您只想始终使用 HTTPS,则可以无条件地设置environ['wsgi.url_scheme'] = 'https'

PREFERRED_URL_SCHEME is not the way to do this. PREFERRED_URL_SCHEME不是这样做的方法。 It's ignored whenever a request is in progress . 每当请求正在进行时,就会被忽略

I tried the accepted answer with an url_for arg but I found it easier to use the PREFERRED_URL_SCHEME config variable and set it to https with:我使用url_for arg 尝试了接受的答案,但我发现使用PREFERRED_URL_SCHEME配置变量并将其设置为 https 更容易:

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

since you don't have to add it to every url_for call.因为您不必将它添加到每个url_for调用中。

If your are accessing your website through a reverse proxy like Nginx, then Flask correctly dectects the scheme being HTTP .如果您通过像 Nginx 这样的反向代理访问您的网站,那么 Flask 会正确地检测到方案是HTTP

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

The easiest solution is to configure your reverse proxy to set the X-Forwarded-Proto header.最简单的解决方案是配置反向代理以设置X-Forwarded-Proto标头。 Flask will automatically detect this header and manage scheme accordingly. Flask 将自动检测此标头并相应地管理方案。 There is a more detailed explanation in the Flask documentation under the Proxy Setups section . Flask 文档中的 Proxy Setups 部分更详细的解释 For example, if you use Nginx, you will have to add the following line in your location block.例如,如果您使用 Nginx,则必须在location块中添加以下行。

proxy_set_header   X-Forwarded-Proto    $scheme;

As other mentionned, if you can't change the configuration of your proxy, you can either use the werkzeug ProxyFix or build your own fix as described in the documentation: http://flask.pocoo.org/docs/0.12/deploying/wsgi-standalone/#proxy-setups正如其他人提到的,如果您无法更改代理的配置,您可以使用 werkzeug ProxyFix 或按照文档中的说明构建您自己的修复程序: http ://flask.pocoo.org/docs/0.12/deploying/ wsgi-standalone/#proxy-setups

Setting _scheme on every url_for() call is extremely tedious, and PREFERRED_URL_SCHEME doesn't seem to work.在每个url_for()调用上设置_scheme非常乏味,而且PREFERRED_URL_SCHEME似乎不起作用。 However, mucking with what the request's supposed scheme is at the WSGI level seems to successfully convince Flask to always construct HTTPS URLs:然而,在 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)

For anyone ending up here recently there is an official uwsgi fixer for this: https://stackoverflow.com/a/23504684/13777925对于最近在这里结束的任何人,都有一个官方的 uwsgi 修复程序: https ://stackoverflow.com/a/23504684/13777925

FWIW this still didn't work for me since the header wasn't being set correctly so I augmented the ReversedProxied middleware to prefer https if found thusly: 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)

Called as:称为:

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

This way if you've set the environment var "PREFERRED_URL_SCHEME" explicitly or if the nginx/etc/proxy sets the X_FORWARDED_PROTO, it does the right thing.这样,如果您已经明确设置了环境变量“PREFERRED_URL_SCHEME”,或者如果 nginx/etc/proxy 设置了 X_FORWARDED_PROTO,它就会做正确的事情。

我个人无法使用此处的任何答案解决此问题,但发现只需在 flask run 命令的末尾添加--cert=adhoc即可解决该问题。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM