How to Create a Static Website with Flask
2022-01-05
Static sites are dead simple: simple to make, simple to deploy, and simple to serve. And because they're so simple, there are naturally hundreds of ways to manage them.
Even so, we have excellent and time-tested web frameworks that we can repurpose as static site generators with minimal effort. I made this website with Flask, and in this post I'll show you how.
(You can see this site's source code on GitHub.)
The application skeleton
If you're familiar with Flask already, you can skip this section. If not — behold! Flask is delightfully lightweight:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
render_template
uses Jinja templates by default. Both of my
templates above inherit from base.html
, which defines my boilerplate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Since the end result is a static website, dynamic Flask features like session management and form submissions naturally won't work. This isn't an issue as long as you stick to the basics: routes, templates, and no stateful logic.
Writing posts with Markdown and Pygments
I write posts in Markdown with some extra metadata:
1 2 3 4 |
|
Then I process these posts with python-markdown
, with two extensions:
meta
for post metadata (thetitle
anddate
fields above)codehilite
for code syntax highlighting.
Both of these extensions are provided with the default markdown
package.
Here's the basic setup:
1 2 3 4 5 6 7 8 |
|
codehilite
uses Pygments under the hood. You can generate Pygments CSS from
the command line:
1 2 3 4 |
|
The default CSS works well enough, but I had some issues with its whitespacing around line numbers and made some small changes to the defaults.
Deploying with Frozen-Flask
Frozen-Flask can generally discover app URLs on its own. For most of my use cases, it works out of the box with no extra config:
1 2 3 4 5 6 7 |
|
Then deployment is as simple as running the script above and syncing the output to prod:
1 2 |
|
Scaling this setup
I've scaled this setup by caching rendered text content and re-rendering only when the source file changes. While I haven't used this approach for a truly massive setup with millions of static files, it has been totally sufficient for my needs.
Overall, I love using Flask to create static websites. I can use a mature framework that I love, lean on its rich ecosystem of plugins, and easily move to a dynamic application if necessary.