New Website, New Technology

View Markdown Other Articles

Article written by a human: Mike Cardwell

If you come here occasionally, you may have noticed that the style of my website has changed completely. I've built a new website using Go. The old website was a bloated custom Node.js app that was probably full of keyloggers and bitcoin stealers. It was also difficult to work on, and stored blog posts in MongoDB webscale!.

This one keeps all of my blog posts in markdown files, making it much easier to write and edit. You will even see a "View as raw markdown" link at the top of each blog post. Hopefully this will give me a little more impetus to post on a regular basis.

Here are some new things that I learnt in the process and some things that I've done which are a little novel:

SVG Sprites

You can stick several distinct images inside a single SVG file, and then reference them individually when displaying them from HTML. For example, if you were to stick the following SVG content at /img/icons.svg:

<svg xmlns="http://www.w3.org/2000/svg">
  <symbol id="blog" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
    <path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20"/>
  </symbol>
  <symbol id="gitlab" viewBox="0 0 24 24" fill="currentColor">
    <path d="m23.546 10.93-.963-2.96-1.907-5.862a.477.477 0 0 0-.906 0l-1.907 5.862H6.137L4.23 2.108a.477.477 0 0 0-.906 0L1.417 7.97.454 10.93a.952.952 0 0 0 .345 1.065l11.2 8.137 11.2-8.137a.952.952 0 0 0 .346-1.065z"/>
  </symbol>
</svg>

That SVG has a "blog" and "gitlab" image inside it. See the id attribute of the symbol tags. If you want to show the "blog" image in your html, instead of using an <img/> tag, do this:

<svg><use href="/img/icons.svg#blog"></use></svg>

The fragment value is used to select the correct symbol.

Early Hints

You can basically respond to an HTTP request with two responses instead of one. The first response is a 103, which tells the client to start fetching some resources that it will need later (unless it already has it in it's cache). The second one is the actual response you requested. This is useful because you might want to tell the browser to start fetching a CSS file immediately, even though it's going to take another 200ms or whatever to render the HTML that you've asked for:

HTTP/2 103
Link: </css/main.css>; rel=preload; as=style; crossorigin=anonymous

HTTP/2 200
...headers

<!DOCTYPE html>
...body

My framework dynamically scans the templates at build time to find content that could be pre-loaded using this method, and makes sure it is. It probably has little impact here as my responses are so quick anyway, but it's interesting to know about.

Speculation Rules

I have a minified version of the following in the head of each page on this website:

<script type="speculationrules">{
    "prefetch":[{
        "source":    "document",
        "where":     {"href_matches": "/*"},
        "eagerness": "moderate"
    }],
    "prerender":[{
        "source":    "document",
        "where":     {"href_matches": "/*"},
        "eagerness": "conservative"
    }]
}</script>

The above makes it so that when your mouse hovers over an anchor tag pointing to local content, it is pre-fetched over the network. And also, at the point of a mousedown event triggering, it starts pre-rendering that content. No JavaScript required.

Browser Tests

I created a set of Headless Browser tests. If I run make test, it fires up a local copy of the website and visits each page in turn, searching for any errors in the browser console and 404 or 5xx codes etc. I also have tests which will visit all external links to see if any are broken. I've cleaned up quite a few, but there are still some left to do after ~17 years of blogging. I have further tests to check if external links can be upgraded from http to https.

Single Compiled Binary

The whole website is a single executable, containing all of the code, the webserver, the HTML, templates, CSS, images and so on. I use aprice/embed to embed the content inside the binary. Content is even gzip compressed before being embedded where appropriate. It's a 4.6MB binary (UPX compressed). I simply run it, and it listens on port 8080 and serves up all content.

Caching

In my templates, rather than using <img src="example.png"> I put <img src="{{ static "example.png" }}">. My framework has a static template function, which calculates a SHA256 of the content of example.png, and adds a prefix of that as a query string parameter. E.g it might change it to <img src="example.png?v=LBd7xmo56DEcqwlj">. My framework then identifies a request for some static content, with a v parameter in the query string, and makes sure to send a Cache-Control: public, max-age=2592000, immutable header with it. This tells the browser (and my nginx proxy) that this content will not change, and to cache it for 30 days in case it is needed again. If I decide to change the content of that file, the hash will change, and therefore your browser will immediately receive the next version of it, ignoring the previously cached version. The hashes are pre-calculated at build time.

Privacy

Privacy policies are usually not very interesting, but I put some effort into using the web technologies that are available, to secure my visitors privacy. You might learn something interesting from it. For example I send a HTTP response header which stops your browser from reporting to external websites that you came from www.grepular.com when you click one of my external links. See the full policy here.


Support my work: PayPal · Patreon · Bitcoin

← Back to blog index