September 29, 2020

Improved Website Speed

Courtland Allen @csallen

Indie Hackers should be much faster nowadays… at least for some of you!

In the past week, the PageSpeed Insights score for our homepage on mobile jumped from 24 to 78. On desktop it jumped from 40 to 97. Not too shabby!

We had similar improvements on post pages, which are just as important (if not more): from 22 to 79 on mobile, and from 57 to 98 on desktop.

PageSpeed Insights Scores for the IH Homepage

The vast majority of the speedup came from two improvements: (1) serving static pages, and (2) optimizing images.

Static Pages

Indie Hackers is a single-page application (SPA) with server-side rendering. When you visit the site, you have to wait a few seconds for the JS to build the single page app in your browser. For some routes we have server-side rendering, so at least you'll see the full page of HTML while the SPA boots up. On others, you just see a random quote to distract you from how slow it is. 😈

However, It turns out that static HTML is all we really need for anonymous visitors. They don't actually need the SPA to boot up. So I wrote some code that lives on the edge nodes of our CDN. It modifies the HTML response to simply strip out our SPA JS. It also strips out lots of JS and CSS that were only used for the SPA, and replaces them with smaller page-specific JS and CSS.

The result is that, for anonymous visitors, the homepage and post pages load almost instantaneously when they're cached in our CDN. They take a second to load when you get a cache miss, but I've set the cache to last for 7 days, so it should be rare for popular posts. I've also got some good cache invalidation going on, where I'll refresh a post page in the cache when it gets new comments, edited, upvoted, etc. This kind of stuff is a pain to code and maintain, but it's necessary for a dynamic site with lots of visitors to pages that change frequently.

Anyway, this was the vast majority of the speed improvement. In the future I'd like to do the same for logged-in users, but that's a bit trickier, since there's a ton of JS in the SPA that deals with logging you in and allowing you to interact with various components (e.g. leaving comments and upvoting things).

"But Courtland, why did you make IH a SPA to begin with?"

Because it was a fun hobby project when I started it, and I didn't know it would get big leave me alone okay jeez.

Optimizing Images

This was a relatively simple fix, but it took a while. When you upload an avatar to your IH profile, I've got some code it automatically resizes it down to 72x72 pixels. However, I never got around to wring similar code for product and group icons. And if you look at the homepage, it's absolutely littered with lots of these icons, which makes it bright and happy, but also super huge. If you're on a slow mobile connection, the last thing you want is to download 3MB of images every time you visit the site.

So I spent some time optimizing all of these images. First, instead of merely shrinking them to one size, I'm shrinking them to 3 different sizes, and using the smallest possible size wherever I can get away with it. For example, group icons have a 28x28 pixel size for use in the social feeds around the site:

Group Icons in the Social Feed

Second, I'm also converting these images to WEBP, which is a better compression algorithm than both JPG and PNG. The homepage still has way too many images, but now they're just a few kilobytes each on average, whereas before they were often dozens or hundreds of kb each.

Besides the above, there were a lot of smaller things, too. And my list of potential speed improvements is still super long. But I'm happy enough with the progress so far to move on to other things for a little bit.

  1. 2

    RIP Moon. Speed was its core value add over the website until now. 😅

    Happy to see the progress though!

  2. 2

    Nice improvements! Having spent many days in the page speed insights I know how annoying those numbers can be to budge so you've done really well.

    In my day job, I'm also maintaining a SPA setup with complex and dynamic caching, pre-rendering and server-side rendering. You sure learn a lot 😄

    The LCP time seems quite high on mobile still, having a quick look it seems like the LCP is on the top banner text, which is a consequence of the 'font flash'.

    You could lower the mobile LCP time quite easily:

    • Google fonts updated their recommended font implementation. Using v2 of the google font API with the display=swap parameter would allow the site to show a system font while the custom fonts are loaded in. (for example https://fonts.googleapis.com/css2?family=Source+Sans+Pro:[email protected];300;400;600;700;900&display=swap). You could also get all of the fonts in one request. See the docs
    • Use Preload on the google fonts so they can load before having to load the home.css file
    • Likewise, you can use DNS Prefetching to speed up the request (may not be needed with the above)

    Ignore the above if you're already across it but may be useful for other IH's as performance can feel like a bit of a rabbit hole at times.

    Also not sure if it's related but the social icons on mobile aren't aligned how they were before.

    1. 2

      Thanks, great tips! I hadn't looked into the font stuff yet. Will deploy your improvements soon!

      1. 1

        Nice, looks good 82/100 👌

        If you really want to squeeze that number I think you could get the LCP 240ms quicker if you were to disable the 'puff-in' animation.

  3. 1

    Thanks for sharing the details!

  4. 1

    About images size optimization, do you know Rokka https://rokka.io ?

  5. 1

    "Because it was a fun hobby project when I started it, and I didn't know it would get big leave me alone okay jeez."
    Does this mean you would not have used SPA if you knew it was going to be this big? What would you have preferred to use then? What do you use for backend?

  6. 1

    can you share your tooling and any apps that you used to achieve these results? open source projects? libraries, etc? thanks!

  7. 1

    Wow! That's almost 3X improvement. No more slow Indie Hackers 👍

    @csallen if you want to track pagespeed insights over time, I have built Simple Ops just for that, so you know how much it's improving over time with every change!

  8. 1

    Thanks for the improvements Courtland. I guess people will be curious to know how much impact page speed will have on SEO.

  9. 1

    @csallen I am using Safari on my iPad and WebP images aren’t compatible and I’m getting a lot of blank images.

    Have you added a function to fallback to png?

    1. 1

      This comment was deleted 22 days ago.

      1. 4

        Should be fixed now. (cc @BraydenTW)

        Out of curiosity, why use Safari over Chrome and Firefox? It's consistently way behind other browsers on updates, and Apple doesn't seem to care about moving quickly. It's starting to remind me of the bad old days with IE6.

        1. 1

          Chrome, Firefox and any other browser behave exactly like Safari on iOS/iPadOS. Apple allows applications to use only WKWebView (which uses the WebKit engine) for web browsing so they are all essentially Safari :(

        2. 1

          Haha, I see what you mean.

          I just like Safari for simplicity and I use Google for my iPhone 7

        3. 1

          This comment was deleted 22 days ago.

  10. 1

    It is A LOT faster on my old and trusty iPhone SE, thanks!

  11. 1

    Sweet, it's definitely feeling snappier.

  12. 1

    "But Courtland, why did you make IH a SPA to begin with?"

    I was about to ask this 😀

  13. 1

    Awesome, looks like a pretty decent improvement, at least from what I can remember.
    It's always fun to dive into the different optimizations and see the impact.

    Do you use any lazy-loading on routes or images? I couldn't see any, it can help reducing many initial requests for the images/avatars/badges.

    Also looks like you just cache the images for 1 hour, I don't see why not caching those to 30 days or more since they hardly change.

    If I were you I would dump the quotes when moving between routes, assuming it's now (finally) fast enough to move around anyways.

    1. 1

      Yep definitely need to cache images for longer. I had lazy image loading for a bit, but temporarily removed it. Will add it back.

      Can't get rid of the quotes for routes until I add server-side rendering for those. For now that's basically just the homepage, the start page, posts, milestones, and individual interviews. I'll expand it eventually, but those pages get the vast majority of the views on IH.

      1. 1

        Yeah you can probably just use browser-level lazy-loading: https://web.dev/browser-level-image-lazy-loading/

        This would save tons of requests for the milestone badges and avatars.
        When I'm logged in, some of those seem to load twice in the network panel, so perhaps it's worth a check.

        Didn't realize the quotes are just on those second tier routes, but yeah it would make sense to eventually get rid of those and get the browsing a bit faster on those.

Today's Top Milestones
  • New library - ICONZ
    I am so happy to announce that we created a new premium 3D library ICONZ 🎉 🎉 After months of work and preparation, it is finally here. I believe ICO
  • Crossed 500$ in book sales
    The book I wrote in May/June just crossed 500$ in sales. I put the book on Gumroad first as this was the easiest and cheapest way to share my work wit