Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

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

However, when I changed the page to a https connection, the url_for still gives me http.

I would like to explicitly ask url_for to add https at the beginning of a URL.

Can you point me how to change it? I looked at Flask docs, without luck.

like image 877
Blaise Avatar asked Feb 11 '13 10:02

Blaise


People also ask

How do I redirect to https flask?

replace to replace 'http://' with 'https://' in the URL. We call redirect with the new url with 'https://' and we set the response code to 301 to do a 301 redirect to the https URL from the http URL.

How do you change the URL on a flask?

The url_for() function is used to build a URL to the specific function dynamically. The first argument is the name of the specified function, and then we can pass any number of keyword argument corresponding to the variable part of the URL.

What does flask's url_for () do?

url_for in Flask is used for creating a URL to prevent the overhead of having to change URLs throughout an application (including in templates). Without url_for , if there is a change in the root URL of your app then you have to change it in every page where the link is present.


2 Answers

With Flask 0.10, there will be a much better solution available than wrapping url_for. If you look at https://github.com/mitsuhiko/flask/commit/b5069d07a24a3c3a54fb056aa6f4076a0e7088c7, a _scheme parameter has been added. 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://. 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.


However, consider making your website HTTPS-only instead. 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. This is similar to mixed content.

like image 52
Markus Unterwaditzer Avatar answered Sep 21 '22 22:09

Markus Unterwaditzer


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/

(This Flask bug seems to confirm that that is the preferred way.)

Basically, if your WSGI environment has environ['wsgi.url_scheme'] = 'https', then url_for will generate https: URLs.

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. My solution (specific to Elastic Beanstalk) was like this (simplified from the snippet linked above):

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. Other environments would have other ways of determining whether the external URL included https. If you just want to always use HTTPS, you could unconditionally set environ['wsgi.url_scheme'] = 'https'.

PREFERRED_URL_SCHEME is not the way to do this. It's ignored whenever a request is in progress.

like image 44
aldel Avatar answered Sep 20 '22 22:09

aldel