Skip to main content

How To Eliminate Render Blocking Resources

· Updated on · 15 min read

Render blocking websites can slow down your website and increase Core Web Vitals metrics like the Largest Contentful Paint.

This article explains the performance impact of render blocking resources and what you can do to solve these issues.

What are render blocking resources?

Browsers load a large number of resources when loading a web page, for example CSS files or images. If a resource is render blocking then the browser doesn't start showing page content until that resource has finished loading.

JavaScript and CSS files can be render blocking.

The screenshot below shows a rendering filmstrip and request waterfall for a website. While the request for the HTML document finishes after 600 milliseconds, the browser still shows a blank page at that point.

The browser only starts rendering the page once three additional resources have finished loading:

  • A JavaScript file (c4.min.js)
  • A CSS file (substack.css?v=29***)
  • Another CSS file (substack.css?v=a9***)

You can test your own website using the DebugBear speed test.

Render blocking JS and CSS on Substack

How do render blocking resources impact site speed metrics?

Rendering blocking resources delay rendering milestones like the First Contentful Paint and the Largest Contentful Paint.

How much a render blocking resource impacts page speed depends on a few factors:

  • How large is the resources being downloaded
  • Is a new server connection required to load the resource
  • Is there a chain of render blocking resources (see below for more on this)

Impact on Core Web Vitals and SEO

As LCP is a Core Web Vitals metric, having too many render blocking resources can hurt your Google rankings.

However, if you delay loading resources until later that could also cause layout shift when the resource is finally loaded.

Website test screenshot

Run A Free Page Speed Test

Test Your Website:

  • No Login Required
  • Automated Recommendations
  • Google SEO Assessment

What are render-blocking request chains?

A render blocking request chain happens when a render blocking resource triggers a request for another render blocking resource.

In this example the render blocking CSS file is loaded after 400 milliseconds. But that CSS file uses @import to reference another CSS file. This file also needs to be downloaded before the page renders after 700 milliseconds.

The longer these chains are the bigger the performance impact will be.

Render blocking request chain with CSS @import

Notably, the second CSS also requires a new server connection to be established to fonts.googleapis.com. Because of this the request will take longer than if another resource from discord.com had been loaded.

What does parser blocking mean?

Scripts and stylesheets referenced in the head always block all rendering (at least if they are loaded synchronously). These are called initial render blocking.

But what about resources referenced in the body? Chrome marks those as in_body_parser_blocking. Whether they block render depends on where in the body they appear.

If they are placed at the end of the body tag then parser blocking scripts don't block rendering. But if a parser blocking script appears at the top of the body tag the script will block rendering.

Parser blocking resources are also called "subsequent render blocking".

Parser-blocking JavaScript request

How to identify render-blocking resources

Explore our post Visualize your Website's Blocking Scripts to learn more about how to identify render blocking resources using Chrome DevTools and DebugBear.

Many articles say that JavaScript and CSS files in the head are render blocking. That's a good heuristic but that's not always the case (for example if the async attribute is used, as we'll see later on in this article).

Let's take a look at how different tools report render-blocking requests.

DebugBear and WebPageTest

DebugBear highlights resources that are parser blocking.

Render blocking badges on DebugBear

The color of the badge indicates to you which resources you should prioritize for optimization.

  • A orange parser blocking badge means that the resource comes before the main page heading (the <h1> tag) and could block rendering of important page content.
  • A gray parser blocking badge means that the resource comes after the main page heading. Depending on where this resource is, it may not block rendering of important page content.

When you open up a resource from the DebugBear request view, you get extra metadata including the render blocking status, and the position of the resource in the document relative to the <h1> tag.

Render blocking metadata on DebugBear

We've already seen how DebugBear highlights render blocking requests. We use the data that Chrome provides in the ResourceSendRequest trace event.

WebPageTest uses the same data and highlights render blocking requests using an orange badge.

Render blocking badges on DebugBear and WebPageTest

Lighthouse

The Lighthouse report shown on PageSpeed Insights also contains an "Eliminate render-blocking resources" audit.

However, this doesn't use the Chrome data and can sometimes miss render blocking files. For example, in the Discord example the Google Fonts CSS is incorrectly not shown as render blocking.

Lighthouse render blocking audit

How to eliminate render blocking resources

What you need to do to remove a render blocking request depends on the type of resource that's being loaded.

You might need to change script tags to load asynchronously or inline critical CSS.

Render blocking script tags

By default, the browser goes through the document from top to bottom. JavaScript code is run synchronously one script after the other.

For example, in this case the browser will first run chat.js and then analytics.js. The h1 tag is only shown after running the scripts.

<script src="chat.js"></script>
<script src="analytics.js"></script>
<h1>Hello world</h1>

However, many scripts don't need to be render blocking and can be run asynchronously. You can achieve that using the async attribute.

<script src="chat.js" async></script>
<script src="analytics.js" async></script>
<h1>Hello world</h1>

Now the browser will still start loading the JavaScript files as soon as possible, and run them as soon as they are downloaded. But in the meantime, rendering the h1 will no longer be blocked.

Also, if analytics.js finishes loading before chat.js, then analytics.js will run without first waiting for the chat widget code. If you want to maintain the order of execution you can use the defer attribute, which defers JavaScript execution until after the HTML document has been fully parsed by the browser.

This waterfall chart demonstrates the impact that async and defer have on page load behavior.

async and defer in the waterfall

Render blocking CSS

Reducing render blocking stylesheets is harder than reducing render blocking scripts, as a page will often look very different if stylesheets are missing. If you made an important stylesheet load asynchronously you'd get a flash of unstyled content (FOUC).

Page with and without CSS

While loading key CSS files asynchronously is not an option, loading stylesheets for third party code can be more viable, for example if you have a third party widget that's only used further down on the page. In that case updating the styling later on is acceptable. Another candidate for asynchronous CSS loading would be a stylesheet that only loads font references but does not affect the page layout.

Let's say you have this stylesheet in your HTML:

<link rel="stylesheet" href="widget.css" />

This way widget.css will block rendering. To load the stylesheet asynchronously you can initially set the media attribute to print. Then, when the CSS file has loaded, you change it to all to apply the styles to the page.

<link
rel="stylesheet"
href="widget.css"
media="print"
onload="this.media='all'"
/>

Inlining critical CSS

Another way to remove render blocking CSS files is to embed the styles directly in the HTML document. This will increase the size of the HTML, but it can be a great solution for small CSS files under 10 KB.

Here's an example website where render blocking CSS is inlined into the page HTML. The page renders immediately after the document is loaded.

Page rendering immediately after the document request

When we look at the page HTML we see a large inline style tag.

Inline style tag

The downside of this approach is that the CSS has to be downloaded again with every HTML request, while a separate CSS file could have been in a cache. How bad this is depends on the amount of CSS being inlined.

Website monitoring illustration

Monitor Page Speed & Core Web Vitals

DebugBear monitoring includes:

  • In-depth Page Speed Reports
  • Automated Recommendations
  • Real User Analytics Data

How to reduce the performance impact of render blocking resources

Often not all render blocking resources can be eliminated. But you can still reduce the impact they have on performance.

Reduce file size

Downloading large files takes longer than downloading small files. Therefore, reducing the size of critical requests can speed up your website.

Large file downloads take more time

There are a few ways to reduce file size:

  • using better content encoding, e.g. switching from gzip to brotli
  • making sure only the most important content is included in blocking files, and additional content can then be loaded later on

The Chrome DevTools Coverage tab can help you identify and remove unused CSS and JavaScript code on your page.

Chrome DevTools Coverag tab

Reduce resource competition

Network connections can only provide a limited amount of bandwidth, so check if other resources are competing with the render blocking requests.

Here's an example of what resource competition looks like in a waterfall chart. The c4***.svg image is quite small, only 21 kilobytes. But the browser is allocating bandwidth to the JavaScript file below it, loading over 2 megabytes of data. So the SVG only finishes loading once the JavaScript resource has finished loading.

Network resources competing with each other

Reuse server connections

Connecting to a new server requires the browser to do a DNS lookup, establish a TCP connection, and enable a secure SSL connection. Each of these steps requires at least one round trip on a network. The browser can only start making the HTTP request once the connection is established.

For example, the Substack website is located on substack.com but then loads additional render blocking resources from sentry-cdn.com and substackcdn.com.

New server connections for new domains

In contrast, the gov.uk website loads all resources from gov.uk and can therefore reuse the existing server connection.

Reusing existing connections

Reduce request chaining

Render blocking request chains happen when a render blocking resource starts loading another render blocking resource.

CSS @import

We saw this briefly earlier on in this article when we looked at how to identify render blocking files. The Discord homepage used @import to load a stylesheet from Google Fonts.

@import url(https://fonts.googleapis.com/css?family=Press+Start+2P);

The browser first needs to load the Discord stylesheet to discover the Google Fonts file. We can use a preload resource hint to help the browser discover the resource sooner.

<link
rel="preload"
as="style"
href="https://fonts.googleapis.com/css?family=Press+Start+2P"
/>

Adding this hint in the document HTML means the browser will start loading it without first waiting for the Discord CSS file.

This waterfall shows the page requests without the preload and then with the preload. After adding the preload the start time of the fonts CSS requests shifts to the left.

Preload hint

document.write

document.write can cause similar issues for JavaScript as @import does for CSS. If a render blocking script synchronously creates a new script element with document.write then the new JavaScript file will also be render blocking.

This waterfall shows an example where script.js synchronously adds jquery-3.6.0.js to the page and thus delays rendering.

document.write request chain

Again, this could be fixed by using a preload hint. Putting the script tag directly into the document HTML instead of using document.write would also address the issue.

Is the HTML document request render blocking?

The HTML document is at least partially render blocking, as the browser can't show the page without knowing what its contents are. To find that out the web server needs to start sending the HTML code to the client.

Therefore a slow Time to First Byte (server response time) will make your website render more slowly.

However, browsers use streaming parsers that start processing the HTML as soon as it comes in, rather than waiting until the full document has been downloaded. Therefore, pages can start rendering before the document has finished loading.

This example shows that the other resources referenced in the HTML document start downloading before the HTML request has completed.

CleanShot 2022-08-23 at 15 55 36

In this case we don't see the page rendering before the completion of the document request though. That's because downloading the page HTML is a high priority task for the browser, so the other render blocking resources have to compete with that. Due to the focus on loading the HTML, loading the CSS and JavaScript code for the page only happens after the HTML download is complete.

Are web fonts render blocking?

Web fonts don't block rendering of the page, but they can block rendering of the text itself. How text renders before fonts are loaded is specified by the font-display CSS property.

Waiting for web fonts can slow down your First Contentful Paint if no other content is rendered on the page. If your text is still hidden but an image has rendered somewhere on the page then web fonts won't make your FCP worse.

The Largest Contentful Paint will be impacted if the LCP element is a text node using a web font.

Page renders but text doesn't show

Are images render blocking?

Images are not render blocking. They can delay metrics like the Largest Contentful Paint, but the rest of the page will still render fine even if the browser is still downloading an image file.

Why isn't my page rendering after all render blocking resources have loaded?

Sometimes the request waterfall suggests that all render blocking requests have finished, but the filmstrip will still show a blank page. This can have a few reasons.

Is content being hidden with CSS?

Some A/B testing tools set the body opacity to 0 to avoid flicker, delaying when the page renders.

Single page apps

Some sites are pure single page apps with no server rendered content. In those case, even if rendering isn't blocked, there isn't any content to render until the JavaScript application has been loaded and run.

To fix this, consider rendering a header on the backend or at least embedding a loading spinner in the page HTML to indicate the browser is waiting for the JavaScript app to load.

Parser blocking resources

As mentioned above, synchronous scripts or stylesheets in the body block all rendering of content below that tag. This isn't a problem if the tags are placed at the end of the body tag, but if they appear before important content then rendering will be delayed.

Monitoring rendering milestones

DebugBear can help you detect render blocking resources, optimize your site speed, and monitor Core Web Vitals and other performance metrics over time.

Website rendering timeline

Once you've reviewed your metrics you can investigate test results in depth. Start a free trial today.

Rendering timeline

Get a monthly email with page speed tips