使 Flask 的 url_for 使用 'https'AWS 负载均衡器中的方案,而不会与 SSLify 混淆 [英] Make Flask's url_for use the 'https' scheme in an AWS load balancer without messing with SSLify
问题描述
我最近向我的 web 应用程序添加了 SSL 证书.它部署在 Amazon Web Services 上,使用负载平衡器.负载平衡器作为反向代理工作,处理外部 HTTPS 并发送内部 HTTP.因此,尽管连接是安全的,但我的 Flask 应用程序的所有流量都是 HTTP,而不是 HTTPS.
I've recently added a SSL certificate to my webapp. It's deployed on Amazon Web Services uses load balancers. The load balancers work as reverse proxies, handling external HTTPS and sending internal HTTP. So all traffic to my Flask app is HTTP, not HTTPS, despite being a secure connection.
因为网站在 HTTPS 迁移之前已经上线,我使用 SSLify 发送301 永久重定向
到 HTTP 连接.尽管所有连接都是 HTTP,但它仍然有效,因为反向代理将 X-Forwarded-Proto
请求标头设置为原始协议.
Because the site was already online before the HTTPS migration, I used SSLify to send 301 PERMANENT REDIRECTS
to HTTP connections. It works despite all connections being HTTP because the reverse proxy sets the X-Forwarded-Proto
request header with the original protocol.
url_for
不关心 X-Forwarded-Proto
.当方案不可用时,它将使用 my_flask_app.config['PREFERRED_URL_SCHEME']
,但在请求期间方案可用.与反向代理连接的HTTP方案.
url_for
doesn't care about X-Forwarded-Proto
. It will use the my_flask_app.config['PREFERRED_URL_SCHEME']
when a scheme isn't available, but during a request a scheme is available. The HTTP scheme of the connection with the reverse proxy.
因此,当有人连接到 https://example.com
时,它会连接到负载均衡器,然后负载均衡器使用 http://example.com
连接到 Flask.Flask 看到 http
并假定方案是 HTTP,而不是原来的 HTTPS.
So when someone connects to https://example.com
, it connects to the load balancer, which then connects to Flask using http://example.com
. Flask sees the http
and assumes the scheme is HTTP, not HTTPS as it originally was.
在模板中使用的大多数 url_for
中这不是问题,但是任何带有 _external=True
的 url_for
将使用 http 而不是 https.就个人而言,我将 _external=True
用于 rel=canonical
,因为我听说这是推荐的做法.除此之外,使用 Flask.redirect
将在非 _external url 前面加上 http://example.com
,因为重定向标头必须是完全限定的 URL.
That isn't a problem in most url_for
used in templates, but any url_for
with _external=True
will use http instead of https. Personally, I use _external=True
for rel=canonical
since I heard it was recommended practice. Besides that, using Flask.redirect
will prepend non-_external urls with http://example.com
, since the redirect header must be a fully qualified URL.
例如,如果您重定向表单帖子,就会发生这种情况.
If you redirect on a form post for example, this is what would happen.
- 客户发帖
https://example.com/form
- 服务器向
http://example.com/form-posted
发出 - SSLify 然后向
https://example.com/form-posted
发出
303 SEE OTHER
301 PERMANENT REDIRECT
由于 SSLify,每个重定向都会变成 2 个重定向.
Every redirect becomes 2 redirects because of SSLify.
https://stackoverflow.com/a/26636880/1660459
my_flask_app.config['PREFERRED_URL_SCHEME'] = 'https'
不起作用,因为在请求期间有一个方案,而使用该方案.见 https://github.com/mitsuhiko/flask/issues/1129#issuecomment-51759359
Doesn't work because there is a scheme during a request, and that one is used instead. See https://github.com/mitsuhiko/flask/issues/1129#issuecomment-51759359
https://stackoverflow.com/a/28247577/1660459
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)
按原样,这不起作用,因为我稍后需要该应用程序.所以我改用 wsgi_app.
As is, this didn't work because I needed that app later. So I used wsgi_app instead.
def _force_https(wsgi_app):
def wrapper(environ, start_response):
environ['wsgi.url_scheme'] = 'https'
return wsgi_app(environ, start_response)
return wrapper
app = Flask(...)
app.wsgi_app = _force_https(app.wsgi_app)
因为 wsgi_app
在任何 app.before_request
处理程序之前被调用,这样做会使 SSLify 认为该应用程序已经在安全请求之后,然后它不会执行任何 HTTP-to-HTTPS 重定向.
Because wsgi_app
is called before any app.before_request
handlers, doing this makes SSLify think the app is already behind a secure request and then it won't do any HTTP-to-HTTPS redirects.
(我什至找不到我从哪里得到的)
(I can't even find where I got this one from)
from functools import partial
import Flask
Flask.url_for = partial(Flask.url_for, _scheme='https')
这可以工作,但如果你设置了 _scheme
而不是 _external
,Flask 会报错.由于我的大部分应用 url_for
都是内部应用,因此它根本无法运行.
This could work, but Flask will give an error if you set _scheme
but not _external
. Since most of my app url_for
are internal, it doesn't work at all.
推荐答案
我最近在使用 AWS Elastic Load Balancer 后面的 `redirect(url_for('URL'))' 时遇到了同样的问题 &我使用 werkzeug.contrib.fixers.ProxyFix 解决了这个问题调用我的代码.例子:
I was having these same issues with `redirect(url_for('URL'))' behind an AWS Elastic Load Balancer recently & I solved it this using the werkzeug.contrib.fixers.ProxyFix call in my code. example:
from werkzeug.contrib.fixers import ProxyFix
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
ProxyFix(app.wsgi_app)
向未设计 HTTP 代理的应用程序添加 HTTP 代理支持.它从 X-Forwarded 标头设置 REMOTE_ADDR、HTTP_HOST.
The ProxyFix(app.wsgi_app)
adds HTTP proxy support to an application that was not designed with HTTP proxies in mind. It sets REMOTE_ADDR, HTTP_HOST from X-Forwarded headers.
示例:
from werkzeug.middleware.proxy_fix import ProxyFix
# App is behind one proxy that sets the -For and -Host headers.
app = ProxyFix(app, x_for=1, x_host=1)
这篇关于使 Flask 的 url_for 使用 'https'AWS 负载均衡器中的方案,而不会与 SSLify 混淆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!