Have you ever wondered what it would take to develop a simple web app? Maybe you wanted to create one that would serve requests to merely call a command-line version of an app you made. This is what I wanted to do after I created a Word Maze Generator to help my son study for his 3rd-grade spelling tests. With Gunicorn and Nginx, I easily accomplished this task.
Creating web front-ends for simple applications is an excellent way to reach a larger audience and get feedback on your work. In my case, I wanted to make my application available to my son’s teacher and classmates.
Here, I am focusing on the ‘why’ more than the ‘how.’ For a practical guide, feel free to navigate to this Digital Ocean post that I used to implement these tools. If you are like me though, the ‘why’ is very important, so let’s dive in!
Web Request, What?
Below is the ideal model for what happens when you hit Enter on your keyboard with a URL in your web browser.
There are steps like Domain Name Resolution that enable this ideal model, but for our purposes, let’s stick to this simple construction.
When you enter in www.periaptapis.net and hit Enter, a Hyper-Text Transfer Protocol (HTTP) GET request is assembled by your browser and shipped over the network to the server (host) responsible for that domain. Once the host receives the request, it packages the content and ships it to the client who requested it.
Naturally, this leads to the question: “So all I need is a web host that is connected to the Internet and running my Flask app, right?” The answer to this question is technically “yes.” However, give it time plus a small amount of traffic, and your site is bound to run into some issues.
WSGI Time!
Let’s say there are three people requesting content from your website. If it’s a small application, you might be able to service that traffic in a semi-timely manner. However, some users are going to ‘hang’ until your website can catch up. Some might possibly be left with a page informing them that their request has timed out and to try again (the problem will only get worse if they do!).
Web Server Gateway Interface, or ‘WSGI,’ was created to solve exactly this problem.
In our new model, we show how our three requests go to a single Gunicorn application. Gunicorn translates the web request to the WSGI format and creates threads/processes to distribute the requests in queue. The data that your application returns is then translated to an HTTP response by Gunicorn and sent back to the client.
It helps to be able to increase the number of programs working on the requests, but there is a ceiling to the number of threads/processes that can be created on a given web host.
Let’s consider 1000 requests. Gunicorn, while mighty, cannot service this kind of demand. It is meant for WSGI translation and process/thread management to call your application. Gunicorn is better than just a single-threaded Flask application, but it is still overwhelmed by a large set of requests.
Enter Nginx
Nginx is a program that handles the interface between the world-wide Internet and Gunicorn. It can juggle many connections at once, routing them as configured. Our final setup looks like this:
It is a bit excessive to say we could handle a bajillion requests from our gaggle of Internet users, but we are much better off this way. With Nginx installed, our CPU-dependent application can be spun up and managed by Gunicorn while Nginx keeps the users happy, reassuring the client that we are definitely still working on their request and will get back to them.
An added benefit is that with minimal extra work, we get the nice little lock symbol in the browser, which indicates an HTTPS connection with our service. To do this, we run a tool called Certbot after we have Nginx configured. Certbot asks for the server that needs certificates and the owner’s email address. Finally, Certbot requests your certificate from a certificate authority, which is then auto-configured in your Nginx setup for HTTPS traffic. Awesome!
Final Remarks
Check out the current iteration of our spelling maze app front-end here. It employs the exact setup described in this article to create custom spelling mazes requested by users.
The practical implementation I followed to get my server up and running can be found in a Digital Ocean post here.
If you are curious about the monstrous lead photo, it came from an AI model called Stable Diffusion 2, run with the prompt: “Nginx, Gunicorn and Flask.”
Thank you for reading this post. I hope it puts the ‘why’ into the methodology I chose to serve a web application to multiple users. That being said, I am not an authority on web dev! I’m always looking for new information, so if you want to add to or correct anything in this post, please leave a comment!
Kool stuff!