Making a super-fast simple website

I’ve wanted to redo my personal homepage poeschko.com in a minimalist yet modern way for a while. As a fun little exercise, I also set out to make it as fast as possible, fully loading in less than a second, without making design sacrifices.

Probably one of the simplest websites out there. But there’s a little more going on behind the scenes than one might guess.

Designing the basics

The main title should be a bit extravagant, so I chose a hand-writing font Mr De Haviland on Google Fonts, giving the site a bit of a personal touch. Other text simply uses the system font Georgia, falling back to Times or another serif font.

Since the primary purpose of the site is to assemble links to my profiles on Twitter, GitHub, and LinkedIn, I wanted those links to be at the center of the page. I found some nice minimal free social icons on Iconfinder.

Of course, a modern website ought to be responsive and work well on both small and large screens. Since my site doesn’t contain a lot of text, I wanted it to become a little larger so it fills out the available space when there is more, while still being readable at small widths. I little trick to implement that is to use a CSS calc expression such as

font-size: calc(16px + 10vw);

for the main title, which ensures that the text is always at least 16 pixels tall, while also growing at a rate of 10% of the page width. See Viewport Sized Typography for more info how to add a proper fallback for browsers that don’t support vw.

The site was nice and small so it loaded in a decently fast ~2 seconds without any special optimizations. But I wanted to go the extra mile and make it as fast as possible…

Making it fast

My main realization: Network requests are costly — big surprise, I know.

There’s a trade-off between loading separate resources such as CSS and fonts so they can be cached independently, and including them right in the initial response so the browser doesn’t have to make another network request. For a personal page like mine, the same client will realistically not visit it too often, making caching less important. So I decided to inline all the things!

I started with some CSS that was in a separate file originally. Even if it wasn’t needed right away, it’s just not worth another network request for less than 500 bytes.

Next up: the SVG icons. They’re less than 2 KB each and they are needed right away, so saving three extra network requests by inlining the SVG in the page’s HTML makes sense.

The biggest potential was loading the web font, though. It was large, loaded from another domain, and delaying the time until the page was fully rendered. Even if the main text (using the system font Georgia) was rendered before, the title would only come in later, causing the other text to shift around a bit. The steps to optimizing this:

  1. Host the font on my own server instead of Google Fonts. This sacrifices any chance of the font already being cached, but avoids an extra DNS lookup, SSL handshake, etc. Also, I’m still a bit unsure about the GDPR compliance of Google Fonts, althought this discussion suggests it should be fine.
  2. Subset the font. Since we only use the font to render the text “Jan Pöschko”, we only need a small fraction of its glyphs. Aggressive subsetting wouldn’t make sense for a (potentially cached) font on a CDN used for a lot of varying text, but that’s not the case here. So I used an online Font Subsetter with only the needed characters and then the woff2 tools to turn the resulting TTF file (still useful for older browsers) back into a (smaller) WOFF2 font (which works in all modern browsers). This brings the font size down from 17 KB to 2,132 bytes.
  3. Inline the font as a data URL. Since the font is now really small, we can inline it to safe another network request — at least for modern browsers that support WOFF2. I still include a separate TTF URL for browsers that don’t support WOFF2. I used an online Data URL Maker for this. Base64-encoding the binary font data blows it up a little bit (to 2,867 bytes), but that’s still worth it.

Now, the whole page is contained in one 12 KB blob of HTML, only requiring a single request to the server. It loads and renders in about 800 milliseconds even on a slow 3G connection. Yay! 🎉

Keeping it simple

A lot of the optimizations above could be automated. For instance, there are webpack loaders and plugins that automate subsetting of fonts and inlining of URLs. But I intentionally wanted to keep the creation and maintainence of this (simple) website as simple as possible, not even requiring any build step. I didn’t mind jumping through some manual hoops (once), such as creating data URLs myself. For a more complex project, the strategy would probably be different.

Inlining all styles and scripts in HTML would make a biggger project hard to maintain, but it wasn’t a problem for this small one.

There’s also an Apache PageSpeed Module that would do a lot of these optimizations on the fly when serving an HTML page, but it happens not to be supported by my (simple) web hoster. Besides, it would have taken away the fun of doing things myself.

That being said, I did hook up another Apache module, namely mod_deflate, to serve a gzip-compressed responses if the client supports it. This is the entry in .htaccess:

AddOutputFilterByType DEFLATE text/html

Measuring performance

To quickly measure website performance, an invaluable tool are the browser devtools, e.g. Chrome’s Performance and Network tabs. Chrome also has a convenient Audit panel powered by Lighthouse. It not only measures performance but also gives many other recommendations how to improve one’s website, e.g. in terms of accessibility.

There are also various online tools to generate performance reports, e.g. GTmetrix which includes a YSlow analysis. You can go directly go to the latest performance report for poeschko.com: 0.7 seconds Fully Loaded Time, 6.72 KB Total Page Size (this is after gzip compression), and 2 network requests (this includes the request for the favicon). Got a perfect 100% PageSpeed Score, and an almost perfect 98% YSlow Score.

The latter is due to some missed potential for minifying the contents of <style> and <script> tags — something I didn’t bother with since gzip compression would almost alleviate it anyway.

Conclusion

So there it is, a super-simple website that loads in less than a second while still using a custom web font for some personal touch and a few images. You would probably do things differently in a larger project, but some of the core principles — e.g. trying to minimize the number of network requests — still apply.

There was even time and space for a little easter egg on the site. Can you find it?

All code and its history is available on GitHub.

Good luck with your own website!