Redirect HTTP to HTTPS With Python

I was working on a FastAPI project and I had to deploy it to a web server to make it easily accessible.

I used certbot to generate a free Let's Encrypt certificate and I served the project using uvicorn. uvicorn is the default server included in FastAPI’s documentation.

Usually, production APIs are served using a full-fledged web server such as nginx. The recommended way to deploy FastAPI is their Gunicorn-based Docker image.

My small project will only be used by a small number of persons, so I decided to just use uvicorn.

First, I will show you how to serve your API over HTTPS. Then, I will show you how to use Python to redirect HTTP to HTTPS.

Serve FastAPI over HTTPS #

We will use a tool called certbot. It automatically generates Let’s Encrypt certificates.

You need to point your DNS A record to the IP address of the server that will host your app. I used an Amazon Lightsail VPS instance running Ubuntu 20.04 LTS for my project.

The instructions in my case are located at https://certbot.eff.org/lets-encrypt/ubuntufocal-other. Use the drop-down menu at the top of the page to choose your distribution.

A summary of the commands to run for Ubuntu 20.04 is the following. Use the link above for more details; or to choose a different distribution.

# Certbot installation
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

I didn’t have a running web server so I used the standalone method:

sudo certbot certonly --standalone --register-unsafely-without-email

If you are not deploying for production, then you can use the flag --register-unsafely-without-email to avoid typing your email address. You can read about the “implications” of this flag in this thread.

At this stage, your Let's Encrypt certificate should be ready to be used. uvicorn provides two command-line options to serve your certificate.

  • --ssl-certfile — used to specify your fullchain.pem file
  • --ssl-keyfile — used to specify your privkey.pem file

You can find both files in /etc/leysencrypt/live/[yourdomain]/.

You need administrative privileges to serve your app on port 443, which is the default HTTPS port. Make sure to prefix your uvicorn command with sudo.

It’s time to serve the app. Below is an example to serve a FastAPI app located in server.py.

sudo uvicorn server:app \
        --workers 10 \
        --ssl-certfile /etc/letsencrypt/live/[yourdomain]/fullchain.pem \
        --ssl-keyfile /etc/letsencrypt/live/[yourdomain]/privkey.pem \
        --host 0.0.0.0 \
        --port 443

If we visit https://[yourdomain] using a web browser, the certificate should be valid (no browser warning saying that someone is trying to steal your credit card information).

We can now move on to the second part of this post, in which I will explain how to redirect HTTP to HTTPS using Python.

Redirect HTTP to HTTPS with Python #

The main problem at this stage is that many people will manually visit http://[yourdomain]. Their browsers will not force them to use the HTTPS protocol.

For those of you who are familiar with PHP and WordPress, you may be used to editing .htaccess to redirect HTTP to HTTPS.

In my case, I didn’t want to deal with server configuration. Instead, I decided to use a classic imperative approach. 1

I googled the available solutions to redirect HTTP to HTTPS with code, and I found this post in OpenVPN’s website.

Their solution was simple and easy, but I wanted a cleaner approach. Messing with HTTP headers didn’t seem like a clean solution for me. Plus, their print statements without parenthesis are written in Python 2. I decided that this solution is old and not clean enough.

Next, I found a solution in FastAPI’s documentation. The solution described here uses a middleware to redirect all incoming requests to “the secure scheme”.

This last solution is clean, simple, and it just works.

I created a new file called https.py with the following code:

from fastapi import FastAPI
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware

app = FastAPI()

app.add_middleware(HTTPSRedirectMiddleware)

We will now spawn a new uvicorn process to serve the HTTPS redirection middleware on port 80. Port 80 is the default HTTP port.

80 is a privileged port, same as port 443. Only the root user can run servers on those ports. We need to prefix the following command with sudo.

sudo uvicorn https:app \
        --host 0.0.0.0 \
        --port 80

Now, all HTTP visitors will automatically be redirected to HTTPS.

Usually, 301 or 302 redirects are used when redirecting incoming requests from HTTP to HTTPS. By default, HTTPSRedirectMiddleware uses a 307 redirect.

According to Yoast SEO, 307 is the right temporary redirect to use since HTTP 1.1. You can read more about the difference between 301, 302, and 307 redirects in this post.

Conclusion #

In this post, I showed you how to serve your FastAPI app over HTTPS and how to redirect your incoming HTTP traffic to HTTPS without a full-fledged web server.

Python enthusiasts may find this post interesting because it is more convenient to showcase your newly developed FastAPI app with uvicorn.

Browsers and people are becoming more and more demanding regarding web security, so sharing an insecure HTTP link to your app may not be the best idea.

By following the few steps in this post, you were able to secure your demo environment without having to deal with server configuration.

This post is by no means suitable for production apps. Follow the best practices described in FastAPI’s documentation to publish your app on a publicly available server and/or domain.

  1. In contrast with the more popular declarative approach of dealing with server configuration.