About this site

This website is disastrously over-engineered. I made it that way in order to show off my know-how. Below, I highlight some features of this website.

Infrastructure as Code

Manually deploying a site to the web can involve repetitive and error-prone clerical tasks. I wanted to control my own server, rather than use a turnkey hosting provider. At the same time, I wanted to set up this codebase in such a way that I can simply edit my site's content, then type make deploy into my terminal to trigger a complete hands-free deployment. I accomplished these goals with NixOS. (Shoutout to Justinas Stankevičius, whose setup I imitated.)

You can learn more on the NixOS website, but here are the main bullet points. Nix is a language which which excels at representing server configurations, similar to the Dockerfile configuration language. The nix command-line tool interacts with code written in Nix, and can be used to deploy a system as described in a Nix file. NixOS is, in turn, a Linux distribution that uses nix as a package manager.

To drive this toolchain, I am using the morph deployment tool. If you're curious to read this site's Nix configuration, it's mostly contained in the file network.nix in the project repository.

Content Security Policy

This website uses a content security policy (CSP) to regulate which domains may be used to load resources such as scripts and stylesheets. (You can read it by checking out the response headers in your browser's development pane.) This is probably overkill, because this website is not particularly vulnerable in the first place to the kinds of attacks which CSPs prevent against. But on websites with user-generated content, such as social media platforms or forums, a CSP becomes much more important.

HTML, the markup language in which this page is written, relies on "tags" to express how content should be displayed, and to make it interactive. For example, the <em> tag can be used to emphasize text; the HTML fragment <em>important idea</em> renders as important idea. Tags like <button> give the user buttons to press, and the all-important <script> tag embeds JavaScript into the page, which describes the behaviour of rich interactive UIs.

Let's say you run an online forum. If a hostile user of your forum can post text including the raw characters "<" and ">" characters, they could potentially cause other users to load in a script from the hostile user's own website, using a <script> tag. This is called cross-site scripting (XSS). Web developers use various techniques to prevent this from happening, but a CSP serves as a last line of defence.

The way CSP is implemented on this website is the following. Each HTTPS response includes a header titled content-security-policy, which describes where various resources, like scripts and stylesheets, may be loaded from. Even if a hacker could alter a page to include a tag like <script src="www.evil.com/hack.js"></script> to try to hijack the page with the script hack.js, the user's browser would refuse to run that script unless www.evil.com was explicitly approved in the CSP as a source for scripts. This substantially raises the bar for an aspiring hacker to perform a XSS attack.

Static Site Generation

There are three main architectures used to serve websites on the modern web. These are the static site generation (SSG) model, the server-side rendering (SSR) model, and the single-page application (SPA) model. There are various trade-offs between the three models, and it is not unheard of to mix all three in a complex web application.

In order to maximize perceived load speed, this website mixes the SSG and SPA models. This site is statically generated, in the sense that each page is served by an nginx backend as a static file, and it is a single-page app in the sense that each such page hydrates itself with JavaScript to render new pages dynamically in-browser. For many websites, this technique would reduce perceived load times. (Though for a simple static site like this one, it is conceivable that the magnitude of this benefit is small enough to be unnoticeable.) I used the Next.js framework's SSG options to provide this feature.