python flask redirect to https from http

54,292

Solution 1

To me, it appears you're making it more complicated than it needs to be. Here is the code I use in my views.py script to force user to HTTPS connections:

@app.before_request
def before_request():
    if not request.is_secure:
        url = request.url.replace('http://', 'https://', 1)
        code = 301
        return redirect(url, code=code)

Solution 2

According with the docs, after pip install Flask-SSLify you only need to insert the following code:

from flask import Flask
from flask_sslify import SSLify

app = Flask(__name__)
sslify = SSLify(app)

I have done it and it works very well. Am I missing something in the discussion ?

Solution 3

The Flask Security Guide recommends using Flask-Talisman.

$ pip install flask-talisman

Usage example:

from flask import Flask
from flask_talisman import Talisman

app = Flask(__name__)
Talisman(app)

It forces HTTPS by default (from the README):

force_https, default True, forces all non-debug connects to https.


Personally, I got some errors relating to CSP (Content Security Policy) which I disabled with:

Talisman(app, content_security_policy=None)

But use this at your own risk :)

Solution 4

Thanks to answer from Kelly Keller-Heikkila and comment by jaysqrd I ended up doing this in my Flask app:

from flask import request, redirect
...

@app.before_request
def before_request():
    if app.env == "development":
        return
    if request.is_secure:
        return

    url = request.url.replace("http://", "https://", 1)
    code = 301
    return redirect(url, code=code)

I tried the flask_sslify solution suggested by Rodolfo Alvarez but ran into this issue and went with the above solution instead.

If the app is running in development mode or the request is already on https there's no need to redirect.

Solution 5

Here is a flask solution if you are on aws and behind a load balancer. Place it in your views.py

@app.before_request
def before_request():
    scheme = request.headers.get('X-Forwarded-Proto')
    if scheme and scheme == 'http' and request.url.startswith('http://'):
        url = request.url.replace('http://', 'https://', 1)
        code = 301
        return redirect(url, code=code)
Share:
54,292
David Yuan
Author by

David Yuan

Updated on December 08, 2021

Comments

  • David Yuan
    David Yuan over 2 years

    I have a website build using python3.4 and flask...I have generated my own self-signed certificate and I am currently testing my website through localhost.

    I am using the python ssl module along with this flask extension: https://github.com/kennethreitz/flask-sslify

    context = ('my-cert.pem', 'my-key.pem')
    app = Flask(__name__)
    sslify = SSLify(app)
    
    ...
    
    if __name__ == '__main__':
        app.debug = False
        app.run(
        host="127.0.0.1",
        port=int("5000"),
        ssl_context=context
    )
    

    This does not seem to be working however. I took a look in the sslify source code and this line does not seem to be working

    def init_app(self, app):
        """Configures the configured Flask app to enforce SSL."""
        app.before_request(self.redirect_to_ssl)
        app.after_request(self.set_hsts_header)
    

    Specifically the function call to redirect_to_ssl (I added my own print statement under the redirect_to_ssl function and my statement was never printed)

    def redirect_to_ssl(self):
        print("THIS IS WORKING")
        """Redirect incoming requests to HTTPS."""
        Should we redirect?
        criteria = [
            request.is_secure,
            current_app.debug,
            request.headers.get('X-Forwarded-Proto', 'http') == 'https'
        ]
    
        if not any(criteria) and not self.skip:
            if request.url.startswith('http://'):
                url = request.url.replace('http://', 'https://', 1)
                code = 302
                if self.permanent:
                    code = 301
                r = redirect(url, code=code)
                return r
    

    I am pretty new to python. Any ideas?

  • Kelly Keller-Heikkila
    Kelly Keller-Heikkila over 7 years
    I'm not sure how it would redirect more than once, unless you have something else that's redirecting from https back to http somewhere, creating a loop. It should only redirect once.
  • jaysqrd
    jaysqrd almost 7 years
    Reason that @HimanshuMishra ran into issues that they are probably terminating SSL. Checking request.is_secure() is what you want because it respects the X-Forwarded-Protocol header. code.djangoproject.com/ticket/14597
  • jaysqrd
    jaysqrd almost 7 years
    I think the change is as simple as: if not request.is_secure(): as @tuned mentions below.
  • jaysqrd
    jaysqrd almost 7 years
    Should actually be request.is_secure
  • Joshua Wolff
    Joshua Wolff almost 5 years
    I believe flask_sslify is no longer maintained .. see @Eyal Levin 's answer below
  • Yijin
    Yijin over 4 years
    this works for gcp app engine in flex env as well. Great job @edW
  • Thomas
    Thomas over 4 years
    Also added content_security_policy =None. I tried GOOGLE_CSP_POLICY but it didn't work with google analytics. Also my "Twitter follow me" button wasn't working either. I didn't bother investigating more for something that was working as is before
  • vervas
    vervas over 2 years
    This should be marked as the correct answer. Using it with the following flags does not seem to need the before request patch: app = Flask(__name__); app.wsgi_app = ProxyFix(app.wsgi_app, x_host=1, x_proto=1)
  • Nick K9
    Nick K9 over 2 years
    This is fixing the symptom, but not the underlying problem. You need to set up Flask to correctly handle headers in the proxy environment for security reasons. Doing it properly is actually less code anyway.
  • Cam K
    Cam K over 2 years
    I did try this but if my app is running on port 443 (https), why would the flask app be listening at port 80 (http)?
  • Cam K
    Cam K over 2 years
    Is "app.run" still required and do you specify a port?
  • nmz787
    nmz787 about 2 years
    @CamK I thought the same thing before, but now started using cloudfoundry and it is running a load balancer in front of my app (listening on HTTP and HTTPS), so I can't use my own SSL cert anymore since the balancer forwards all traffic to my app on HTTP
  • nmz787
    nmz787 about 2 years
    worked for me in cloudfoundry