How to run a Flask App Over HTTPS, using Waitress and NGINX.  Updated for 2022. 
Tags: python, flask, nginx, ssl
Published: 2020-09-19 23:03:14

Running a Flask app over HTTPS seems like it should be a simple exercise, however, there are not many up-to-date guides covering this topic. Here are the steps I took to run a Flask app over HTTPS.

Here are the basic steps. 

1. Set up a Flask App.
2. Run it with Waitress.
3. Use a reverse proxy with NGINX.
4. Set up an SSL (updated).
5. Configure the NGINX server rules.



This guide assumes you already have a Flask app up and running. If you don't here is a simple Flask App. 

```
from flask import Flask


app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'> A very simple flask server !</h1>"

if __name__ == "__main__":
    #app.run(host='0.0.0.0')
 
```

Running this should give you the following. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2z6cpqgsazwfyw49d705.png)

Okay, we have our Flask app running, why not just use this server? Per Flask documentation:
 
*'While lightweight and easy to use, Flask’s built-in server is not suitable for production as it doesn’t scale well. Some of the options available for properly running Flask in production are documented here.'*

One of the most simple solutions to this is to use [Waitress](https://docs.pylonsproject.org/projects/waitress/en/stable/) Feel free to read the official guide on your own. But for this example we only need to make some minor changes. First install Waitress. 
```
$ pip install waitress
```
Next, we need to make some changes to our Flask app to let it know we are using Waitress. 
```

from flask import Flask
#we import waitress here. 
from waitress import serve


app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'> A very simple flask server !</h1>"

if __name__ == "__main__":
    #app.run(host='0.0.0.0')
    #We now use this syntax to server our app. 
    serve(app, host='0.0.0.0', port=5000)

```
Now we have our Flask app running with a production-ready server. But we still need our SSL. If you don't have one already, you can use [Certbot](https://certbot.eff.org/) or use a self-signed [Cert](https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-in-ubuntu-18-04).

(Updated: Thanks to Ömer Faruk Sancak)

Run these commands to get SSL or skip and do your own process. 

```

sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python3-certbot-nginx


sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

letsencrypt certonly -a webroot --webroot-path=/var/www/yourdomain.com/html/ -d yourdomain.com -d www.yourdomain.com


```



We will make one final change to our Flask app. We need to let waitress know we will be using https. [Documentation](https://docs.pylonsproject.org/projects/waitress/en/stable/reverse-proxy.html). We add `url_scheme='https'` to our app. 

```
from flask import Flask
from waitress import serve


app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'> A very simple flask server !</h1>"

if __name__ == "__main__":
    #app.run(host='0.0.0.0')
    serve(app, host='0.0.0.0', port=5000, url_scheme='https')
```


Now we have our SSL and are ready to use NGINX. If you don't already have it installed [here](https://www.digitalocean.fcom/community/tutorials/how-to-install-nginx-on-ubuntu-18-04#step-5-–-setting-up-server-blocks-(recommended)) is how to set it up. 

Our Web Server rules will be at 

```
etc/nginx/sites-available/default
```

CD there and delete the example. 

These will be our rules.  Note 

`example.com & your.ip`

This is just an example use your own domain/server address

```
server {
    listen 443 ssl;


    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;






    server_name example.com;



 location / {

            proxy_pass http://your.ip.adress:5000;
            proxy_set_header X-Real-IP $remote_addr;


        }


}



server {
    listen 80;

    server_name example.com;

    return 302 https://$server_name$request_uri;
}



```

Note on this line. 

`proxy_pass http://your.ip.adress:5000;`

If you are running your app on a different port you need to make sure these match. For example, if you are using port 3000 it would be 

`proxy_pass http://your.ip.adress:3000;`



Okay, all of the configurations are done. But we need to let NGINX know, so we restart our server like so. 

`sudo systemctl status nginx`

Refresh and you should see the new server running with the SSL. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5k5tpv48gxmvt22ngu26.png)