About Me / This SiteπŸ”—

This site does not use javascript. It is generated from markdown + Tera templates, using the Zola static site generator. For styling I use the wonderful Tailwind CSS utility framework. The two mix well together without too many issues. The site uses Cloudflare for a lot of infrastructure related, dev-opsy type tasks. You can see its system architecture to see how those pieces work together in more detail.

Table of ContentsπŸ”—


This site taxes advantage of a lot of Cloudflare's offerings. They are mostly of very high quality, with famously generous free tiers. At a minimum I'm using Cloudflare's DNS, CDN, Pages, Workers, Firewall, and I hope to use their recently announced Email Forwarding feature soon.

Lately, I've preferred to architect my projects (not just this site) using these products, because they're designed in a way that's very loosely coupled. This makes it easy to start off with only what you need to begin rapidly prototyping at first, but allows you to integrate more things later. Also, their documentation is very good, and best of all their forum is fantastic.


Each part will be discussed more in-depth later, but here's a visual summary tracing the request flow:

Vector drawing, showing my site's system architecture, using cloudflare services
  1. Initial request either: a.) Hits DNS AND/OR b.) Goes directly to WAF (i.e firewall)
  2. Rules are applied in firewall, before passing request to Worker (i.e. serverless)
  3. Based on METHOD, worker either a.) Sends request to some backend (e.g. Airtable) OR b.) Fetches the page from Pages, where the static site is hosted.
  4. Finally, security headers are set on response by the worker, before making it back to client


Requests to my site first hit DNS. In general, knowing a little about DNS is very useful, because you can elegantly route traffic without having to touch the code to your app. For example, I can have https://staging.spenc.es for testing parts of my app, as well as https://spenc.es for what most of the public sees.

One word of caution:

When using Cloudflare DNS + <SERVICE>, sometimes you will need to set the DNS records through that service. E.g. if you have an website through Cloudflare Pages, and you want to make it accessible on the www subdomain as well, you might think, "Great, I'll just add a CNAME (or A/AAAA) record pointing at the same server that my top-level domain is already pointing at. This will work in a DNS sense... the record will resolve to a valid IP, but your connection will just time out. This is because the integration wasn't setup through the service.

To make an analogy, the phone is ringing, but no one's answering. www is a valid phone number (in our analogy), but the service has not been told it should be taking calls at that number, so it doesn't answer and the request times out.


The next major layer in my architecture is the WAF (web application firewall). Here I can set up filters that apply certain rules, like adding a captcha when someone submits a POST to my recruiting form. This can help protect certain endpoints without you having to actually introduce any new code in your application.

Screenshot of settings a firewall rule


Workers are Cloudflare's "serverless" offering. Basically, it's a function that you write, according to a simple API, instead of a server that you maintain.

For my personal site, I currently use workers for two things:

  1. Setting security headers
  2. Handling form submissions for my recruiting form

A lot of static sites will use something like GitHub pages, but they don't offer a lot of control over one of the main abstractions provided to you in HTTP, which are headers.

The logic I use looks something like this:

  1. Worker receives request
  2. Checks Path/Method
  3. If GET, fetch content from static page
  4. If POST, extract form data, etc...
  5. Set Headers and return response

You can read more about what headers I use here.

Word of caution

While I think Cloudflare workers is a great product, it's not perfect, and one huge thing missing is better support for other languages besides javascript. I use typescript which seems to be the next best thing, but really it's only JS that has first class support.

Also, I think a lot of their documentation/examples are not coded in a very realistic way. They're done to communicate the bare essentials of whatever message they need to at that point. I wouldn't however confuse an example you see in the documentation as the canonical way to do something, just because it's there.


This is really just like GitHub pages or anything else that most people are familiar with. It's a service that builds your static page and hosts it. It gets triggered by a push to a GitHub repo, and it supports quite a number of frameworks.

Using Tailwind CSS with ZolaπŸ”—

Tailwind use PostCSS to integrate with a build system, which Zola doesn't support since it's a a single binary with not plugin system. The maintainer recommends a makefile, which is more or less what I do, except I use npm, since I have tailwind as a node dependency anyway.

To make the two work with each other, I run tailwind's standalone build command after Zola's, putting the resulting css in Zola's build output directory (usually public/). This has worked surprisingly well and I haven't had any issues. Although I have a custom npm build script that I use for convenience, my build/run command(s) are more or less the following:

zola build && npx tailwindcss   \
--input <IN> --output <OUT> 

zola serve --interface  \
& npx tailwindcss               \
--input <IN> --output <OUT>     \
--watch <WATCH_1> <WATCH_2>     \
<...> <WATCH_N>

Where usually I'm watching my main tailwind.css file, as well as content/template directories.

Note, --interface is used so I can test my site on my local machine with my actual mobile device.

This Site Doesn't Use Javascript*πŸ”—


Testing Mobile From My Local MachineπŸ”—

(i.e. πŸ“± β†’ πŸ’»)

It's important to test on an actual mobile device. For this, I run squidman on my laptop, and then configure a proxy in my phone's network settings.


  1. Start squidman, take note of squidman's port and laptop's IP
  2. Configure proxy on phone, using laptop's IP + squidman's port from previous step
  3. Navigate to website using laptop's IP + port that my server is running on