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 API
s 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 yourfullchain.pem
file--ssl-keyfile
— used to specify yourprivkey.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.
-
In contrast with the more popular declarative approach of dealing with server configuration. ↩