Flask 的 url_for 生成 http URL 而非 https

76 投票
9 回答
46145 浏览
提问于 2025-04-17 15:29

我在使用 url_for 来生成用户登出后的重定向网址:

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

但是,当我把页面改成 https 连接时,url_for 还是给我返回 http 的网址。

我想明确地让 url_for 在网址前面加上 https

你能告诉我怎么改吗?我查了 Flask 的文档,但没有找到解决办法。

9 个回答

34

我试过用一个 url_for 参数的方法,但我发现使用 PREFERRED_URL_SCHEME 这个配置变量更简单。我把它设置成了 https,如下所示:

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

这样你就不用在每次调用 url_for 时都加上这个参数了。

59

如果你想让所有服务器生成的链接(比如用到的 url_forredirect)都使用特定的URL格式,而不是每次都要单独设置 _scheme,那么“正确”的做法是使用WSGI中间件,像下面这个代码片段那样:http://flask.pocoo.org/snippets/35/

(这个Flask的bug似乎也确认了这是推荐的做法。)

简单来说,如果你的WSGI环境中有 environ['wsgi.url_scheme'] = 'https',那么 url_for 生成的链接就会是 https: 开头的。

我之前从 url_for 得到的链接是 http:// 开头的,因为我的服务器是在Elastic Beanstalk的负载均衡器后面部署的,而负载均衡器和服务器之间是用普通的HTTP通信。我的解决方案(针对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)

这里面和Elastic Beanstalk相关的部分是 HTTP_X_FORWARDED_PROTO。其他环境可能会有不同的方法来判断外部链接是否包含https。如果你想始终使用HTTPS,可以直接设置 environ['wsgi.url_scheme'] = 'https'

PREFERRED_URL_SCHEME 不是实现这个功能的正确方法。它在请求进行时会被忽略

78

在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://..这样的链接,而不是http://。不过,Flask默认只生成路径(没有主机名或协议),所以你需要加上_external=True,才能把/secure_thingy变成https://example.com/secure_thingy


不过,建议你考虑让你的网站只使用HTTPS。看起来你是想对一些“安全”的路由部分强制使用HTTPS,但如果链接到安全页面的页面没有加密,就无法保证你的HTTPS链接不会被更改。这种情况类似于混合内容的问题。

撰写回答