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 yourfullchain.pemfile -
--ssl-keyfile— used to specify yourprivkey.pemfile
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. ↩
By submitting a comment, your IP address will be stored in a database on Cloudflare. If you wish to prevent this, please use a VPN.
Your comment has been submitted and is awaiting moderation.
Loading comments...