10x Performance Increases: Optimizing a Static Site

A couple months ago, I was traveling outside of the U.S. and wanted to show a friend a link on my personal static site. I tried navigating to my website, but it took much longer than I anticipated. There’s absolutely nothing dynamic about it — it has animations and some responsive design, but the content always stays the same. In Italy, at 8mb/s, it was a different picture entirely. I was pretty appalled at the results, ~6s to DOMContentLoaded, and 8.2s for a full page load. There were 50 requests for a static site, with 2.9mb of total data transferred. I was used to my 1Gb/s, low latency internet in Los Angeles connecting to my server in San Francisco.

This was my first foray into optimizations. Up to this point, any time I wanted to add a library or resource, I would just throw it in and point to it with src=”…”. I had paid zero attention to any form of performance, from caching to inlining to lazy loading.

I started looking around for people with similar experiences. Unfortunately a lot of the literature on static optimizations gets dated fairly quickly — stuff from 2010 or 2011 discussed libraries or made assumptions that simply weren’t true anymore, or just repeated the same maxims over and over.

However, I did find two great sources of information — High Performance Browser Networking and Dan Luu’s similar experience with optimizing static sites. While I didn’t go as far as Dan in stripping formatting and content, I did manage to get my page load to be roughly 10x faster, to less than half a second for DOMContentLoaded and only 635ms for full page load.

The Process

The first step of the process was to profile the site. I wanted to figure out what was taking the longest, and how to best parallelize everything. I ran various tools to profile my site and test it from various locations around the world, including:

Some of these offered suggestions on improvements, but there’s only so much you can do when your static site has 50 requests for everything from a spacer gif left as a remnant from the 90s to assets that aren’t used (I was loading 6 fonts and only using 1)

Timeline for my site — I tested this on the Web Archive as I didn’t screenshot the original one, but it looks similar enough to what I saw a few months ago.

I wanted to improve everything that I had control over — from the contents and speed of the javascript to the actual web server (Nginx) and DNS settings.

Optimizations

Minify and Coalesce Resources

The first I noticed was that I was making a dozen requests each for CSS and JS (without any form of HTTP keepalive), and to various sites, some of which were https. This added multiple round trips to various CDNs or servers, and some JS files were requesting others, which caused the blocking cascade seen above.

I used webpack to coalesce all my resources into a single js file. Any time I make a change to my content, it automatically minifies and turns all my dependencies into a single file.

I played around with different options — currently, this single bundle.js file is in the of my site, and is blocking. However, it’s only 829kb, and that includes every single non-image asset (fonts, css, all libraries and dependencies, and js).

The way the site is currently built, it won’t look correct if we only have stylesheets, so I accepted the blocking nature of a single bundle.js. Load times are ~195ms, which is an order of magnitude better than above.

This also had a few added benefits — I was no longer pointing to 3rd party resources or CDNs, so the user would not need to 1) perform a DNS query to that resource, 2) Perform the https handshake, and 3) actually wait for the full download from that resource.

While CDNs and distributed caching might make sense for large scale, distributed sites, it does not make sense for my small static site. The additional hundred milliseconds or so are a worthwhile tradeoff.

Compress Resources

I was loading an 8mb sized headshot and then displaying it at 10% width/height. This wasn’t just a lack of optimization — this was almost negligent usage of users bandwidth.

I compressed all my images using https://webspeedtest.cloudinary.com/ — it also suggested I switch to webp, but I wanted to remain as compatible with as many browsers as possible, so I stuck to jpg.

Improve Web Server — HTTP2, TLS, and More

The first thing I did was transition to https — when I started, I was running Nginx bare on port 80, just serving files from /var/www/html

I started by setting up https and redirecting all http requests to https. I got my TLS certificate from Let’s Encrypt (an absolutely amazing organization that just started signing wildcard certificates as well!)

Justy by adding the http2 directive, Nginx was able to take advantage of all the modern, baked in advantages of the newest HTTP features. Note that if you want to take advantage of HTTP2 (previously SPDY), you must use HTTPS. Read more about it here.

Utilize Caching & Compression Directives

What more could be accomplished through just Nginx? The first things that jump out are caching and compression directives.

I was sending raw, uncompressed HTML. With just a single gzip on; line, I was able to go from 16000 bytes to 8000 bytes, a decrease of 50%.

Additionally, my site changes fairly infrequently, so I wanted the resources to be cached for as long as possible. This would make it so that, on subsequent visits, users would not need to redownload all assets (especially bundle.js).

My updated server config looks like this. Note that I won’t touch on all the changes I made, such as TCP settings changes, gzip directives, and file cache. If you’d like to know more about these, read this article on tuning Nginx.

And the corresponding server block

Lazy Loading

Lastly there was a small change to my actual site that would improve things by a non-negligible amount. There are 5 images that aren’t seen until you press on their corresponding tabs, but that were loaded at the same time as everything else (due to their being in a  tag.

I wrote a short script to modify the attribute with every element with the lazyload class.

So once the document had completed loading, it would modify the tags so that they went from to , and load it in the background.

Conclusion

This improved my page load times from more than 8 seconds to ~750ms on first page load, and an insane ~500ms on subsequent ones. I really recommend reading through all of High Performance Browser Networking — it’s a fairly quick read, and provides an incredibly well written overview of the modern internet, and optimizing at every layer of the modern internet model.

Did I miss anything? See anything that violates best practices or that could improve my performance even more? Feel free to reach out — JonLuca De Caro!


10x Performance Increases: Optimizing a Static Site was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.

read original article here