Although it’s not uncommon to use external resources in our sites, it is important to realize the potential ramifications of doing so. For instance, if we’re not careful, using external resources have the potential to create a single point of failure (SPOF), something that could cripple the way the page is rendered. What is a single point of failure, and how can we avoid it? That’s what we’ll look at below.
What is a SPOF
By definition, a single point of failure is any part of a system that could keep the entire system from working if that part were to fail.
In front-end development, the threat of a single point of failure can come along with the use of external resources. For instance, if we’re utilizing external scripts, fonts, or styles, and they don’t load properly, what effect would that failure have on the rest of the page? If it would keep the page from rendering or functioning, that external file is a SPOF. Years ago, Steve Souders wrote about this possibility, and set up examples showing the impact a failure for each of these resource types would have on the loading of the page.
For instance, if I have an external script in theof my file
and then that script fails to load, this single failure will block the browser’s rendering path—at least temporarily— keeping it from displaying the rest of the page while it’s trying in vain to load the resource. And if this failure happens at the top of the document, the rest of the content below it—which would be pretty much everything—would not show up during that time.
It’s obvious that none of us would want this to happen on our site. So, what can we do avoid this risk?
Audit 3rd-Party Dependencies
First, it’s a good idea to check to make sure you know what external scripts are being used, why they’re being used, and if they actually need to be used. The fewer 3rd-party scripts, the fewer chances of a SPOF.
Load JS Asynchronously
Once you know which external JS files will actually need to be used, you’ll want to make sure that they are loaded in a way that keeps them from blocking the render path. This is done by loading asynchronously.
In the past, one way to do this was by injecting the external script into the document using an inline script. For instance:
<script> var script = document.createElement('script'); script.src = "//example.com/external-script.js"; document.getElementsByTagName('head').appendChild(script); </script>
This was done because the injected script would not block the network and would allow the browser to continue to download assets while it was loading this external resource. However, this approach does have a downside in that the script still has the potential to block the CSSOM (CSS Object Model) while loading.
Thankfully browser vendors have made loading scripts asynchronously much easier in recent years with the addition of the async attribute for the
<script> tag. Now, to load a script asynchronously, all we need to do is this:
<script src=“//example.com/external-script.js” async ></script>
By adding “async” to the tag, we are telling the browser that the script has an unordered execution, meaning that it can be executed whenever. When modern browsers see this attribute, they won’t block the render path while they load the script. Unlike the the previous injection method, it also doesn’t block the CSSOM, which means that not only will it help prevent SPOF, but also can help with performance.
In addition to “async”, “defer” is another attribute that has been around for a while and can be used to unblock the browser parser/renderer in older versions of IE (9 and earlier). Using both together can be useful if you want to support these versions.
<script src=“//example.com/external-script.js” async defer></script>
Move non-async scripts to bottom of the page
It’s important to note that using ‘async’ should only be used by scripts that can be loaded at anytime. Sometimes, though, we may need to ensure that certain scripts are loaded in a certain order, because one may be dependent on another one. If this is the case, do not use the ‘async’ attribute to load them.
Additionally, it is a good idea to move them to the bottom of the page. Since they will be loaded syncronously and will have the ability to block the render path, we’ll want to place them as late as possible so that the other content and resources can be loaded by the browser first. Not only does this help with performance, but it also ensures that if the script fails to load, it will do as little damage as possible. If it’s above the content, everything below it will be blocked from being rendered while the browser tries to load it—if it’s below, at least all the content will show up immediately, even though some functionality may be missing.
Provide Local Fallback
Another safeguard that can be put in place is to provide a local fallback, when possible, for external scripts. For instance,
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script>window.jQuery || document.write('<script src="/js/libs/jquery-2.1.1.min.js">\x3C/script>')</script>
In this case, an external script (jQuery) is being loaded from a CDN, but there is also an additional script afterward that injects a local copy if the first one fails to load. This doesn’t make things any faster, and won’t alleviate any delay that the failed script might cause, but it does guaranteed that any needed functionality will still be provided in a worst case scenario.
Testing for Vulnerability
Along with optimizing the way scripts are loaded, it’s also valuable to test our sites to see how vulnerable they are to a single point of failure. Although you can do this by hand, thankfully there are utilities out there that make this testing much easier.
SPOF-O-matic extension for Chrome - This extension identifies external scripts that have the potential to block the render of the page. It also gives you the ability to emulate in the browser what would happen if those scripts failed.
WebPageTest - Another online option is provided by WebPageTest. Under ‘Advanced Settings,’ there is a ‘SPOF’ tab that allows you ton specify the hosts names you want to simulate failure on. This gives you the ability to run the WebPage Test while simulating how the page would perform if one or more external hosts were down.
SPOFCheck (CLI) - There is also a command line utility available that allows you to check for potential points of failure—including 3rd-party JS files, as well as external fonts.
Not only will these utilities identify potential points of failure, but they also allow you to test out how your site would perform if there did happen to be a failure. Testing out how your site works with certain scripts disabled can be helpful in evaluating if any changes to how the page is coded should be implemented. Ideally, the site would still be functional and usable, even in a worst-case scenario of having external scripts become unavailable.
Third-party script may be needed from time to time, but it’s important that they are used appropriately and in a manner that does not create single points of failure. Hopefully, they never go down, but you’ll want to be prepared in case they ever do.
- Frontend SPOF, by Steve Souders
- Script-injected “async scripts” considered harmful, by Ilya Grigorik