How to resolve “Avoid multiple, costly round trips to any origin” in Google’s Lighthouse Report
The Problem
The issue that this audit addresses is the time it takes for your website to connect to multiple origins (or servers). The scope of this article is to learn how to apply the solution rather than to discuss the underlying technical issues, however I will provide a brief example to show the difference that optimizing these resources can make.
Using this website speed test, I was able to analyze the waterfall/connections view of Strunk Media’s website. Here are the initial results before optimizing anything:
Notice the stripes made of green-ish, orange-ish, and purple-ish lines. This represents the time spent pinging the dns, connecting to the server, and resolving the SSL. The resources associated with each origin can not render until the connection is fully established. The connection process only begins when the browser ‘gets to’ the part of the document that calls for the request. For example, the first origin request (besides our own domain) is for line 16, fonts.googleapis.com. Although the time to establish this connection seems almost negligible, it causes everything after it to delay slightly, which creates a stacking effect. While each connection may only take a few milliseconds, a handful of requests can quickly add up to make a noticeable difference to the user experience. So, how do we fix this?
The Solution
To resolve this issue, we will strategically implement various resource hints, sometimes called browser hints, within our document. Basically, all we want to do is tell the browser that “we will eventually be connecting with these origins, so start making the connection NOW rather than waiting until we render the content later in the document”. Establishing connections is a concurrent process and does not delay or interfere with the rendering process.
The syntax for implementing this is simple, but the strategy could vary depending on a lot of factors. For example, if you are running any dynamic third party scripts, you may have limited control over what connections need to be established. I came across an issue like this with sites that use Adsense, where each page load might make different origin requests and so therefore it’s not practical to call for an early connection of all possible requests.
With that said, let’s walk through a simple example of an origin we know we will always need to connect with on this particular site, Google Fonts.
Here is what the “preconnect” syntax would look like:
<link rel='preconnect' href='//fonts.googleapis.com' />
Ideally, you’ll want to place this code high in the <head> section of your site, BEFORE any code that would require a connection, otherwise the connection process will have already started and your call would be redundant and not save any time.
Now that we’ve added this line, let’s see how it affects the waterfall:
Pre-connections can only begin after the document’s html has been parsed. Any requests that occur very early are less likely to provide much benefit, since those resources are establishing an early connection to begin with. It’s usually later requests where you save most of the time. But let’s look at this one anyway just to start.
Looking at line 14, notice that the Google Font’s connection was barely established any sooner. Why is that? As we mentioned, connections cannot establish until after the blue stripe from line 1 is completed. Before we added the resource hint, this connection was being established within a few milliseconds but was not able to complete within 1.5 seconds of the initial load. After utilizing the resource hint, the connection process was moved ahead very slightly, to the point where it is barely even noticeable from this view, however we can still clearly see that the connection was completed before the 1.5 second mark, quite a significant savings in my book just for a single resource.
Now apply this to other relevant origins and see a greater impact…
The trick is to locate resources that are establishing a connection IMMEDIATELY before rendering, and either preconnect or preload them in the <head>.
Here is the complete connections view (like waterfall, but filtered to only show connections) for Strunk Media, without any resource hints:
There are a handful or resources that could benefit from establishing an early connection. See lines 16, and every line from 18 to 39. Each one has to wait almost a half second for each connection. Now of course these processes are asynchronous so it doesn’t require the sum-of-all-connection-times to complete the process, but it does delay the subsequent content being rendered. Take note of the time where each connection is established and when the content actually starts rendering.
Based on which resources we can fetch earlier, here is the complete lineup of preconnects or preloads. Note: It’s important to keep your preconnects in order as they would be utilized.
<link rel='preconnect' href='//fonts.googleapis.com' />
<link rel='preconnect' href='//cdn-images.mailchimp.com' />
<link rel='preconnect' href='//apis.google.com' />
<link rel='preconnect' href='//www.googletagmanager.com' />
<link rel='preconnect' href='//cdn.callrail.com' />
<link rel='preconnect' href='//www.google-analytics.com' />
<link rel='preconnect' href='//connect.facebook.net' />
<link rel='preconnect' href='//ads.bingads.microsoft.com' />
<link rel='preconnect' href='//snap.licdn.com' />
<link rel='preconnect' href='//www.gstatic.com' />
<link rel='preconnect' href='//js.calltrk.com' />
<link rel='preconnect' href='//advertise.bingads.microsoft' />
<link rel='preconnect' href='//www.google.com' />
<link rel='preconnect' href='//ssl.gstatic.com' />
<link rel='preconnect' href='//px.ads.linkedin.com' />
<link rel='preconnect' href='//www.linkedin.com' />
<link rel='preconnect' href='//dc.ads.linkedin.com' />
<link rel='preconnect' href='//img.secureserver.net' />
Result:
As you can see, many of the origins now have connections established way before the resource is needed. Which means when it’s time to render, it can happen much quicker. There are still a few issues though, and I thought I’d discuss a couple mistakes I made.
First, two of the resources were not affected, the Facebook Connect (line 23) and Bing Ads (line 26). There are also some origins that didn’t show up in the first report but are now present, so we did not add a preconnect snippet for that origin. We briefly mentioned why that sometimes happens – It has to do with 3rd party scripts operating differently each time you load the page, and sometimes they require different connects. For those items, I usually just ignore them, since it is not worth making the calls if there’s a good chance that the connection is not needed.
Regarding lines 23 and 26, I made a typo in the urls “//ads.bingads.microsoft.com.com” and “//connect.facebook.com”. (should be “.net”). I decided to mention this rather than just correct and re-capture the report to show how easy it is to make a small error or typo that could provide the wrong results. With so many ways for this to go wrong, diagnosing issues can be a nightmare even if it’s just a typo.
We Just Saved More Than An Entire Second!
When you look at the connection times for each resource, it’s easy to see how this method saves a lot of time. With each resource, we are able to establish the connection long before the content was needed, which in each case saves anywhere from a few milliseconds to a few hundred milliseconds. In total, we went from an average of just over 7.5 seconds to just over 6.5 seconds for “Document Complete”.
Let’s look at another line, line 20, Google Analytics. Originally, the connection begins around 2.5 seconds and content starts loading at about 3 seconds, and finishes loading just after 3.5 seconds. With the resource hint provided, the connection is already established within a half second, and the content can be rendered immediately which occurred around the 2 second mark and is completed well before 2.5 seconds. Pretty fast!
One More Layer of Complexity…
There are of course other resource hints, one which is often very useful, the preload. Syntax for that looks like this…
<link rel='preload' href='//www.strunkmedia.com/' as="[something]" >
Just as a quick example, there are some image files that get loaded on this page. Normally, for below-fold content, it’s not a high priority to speed up the loading of these resources, but we’ll do one just to see the effects in action.
Here’s the code:
<link rel='preload' href='https://advertise.bingads.microsoft.com/en-us/WWImages/search/en-us/BingAds_Accredited_Badge.png' as="image" >
Before:
After:
Clearly, this method significantly speeds up delivery of this resource. However, it’s not necessarily without repercussion. It’s possible that some resources were slightly deferred as a result of this, but that is contingent on many other variables such as concurrent connections, etc. Also, doing a preload removes the need for a preconnect in many cases, so choose carefully what you decide to implement. It’s best practice to implement these tags conservatively and only when you are sure that it will have a positive impact on performance.