Why Does PageSpeed Insights Still Say "Eliminate render-blocking resources"?
You've minified your CSS files, compressed your images, and even use a CDN. But the PageSpeed Insights report still shows that same old red flag: "Eliminate render-blocking resources." And beneath it, a list of CSS and JavaScript files that, on the surface, don't seem "essential" at all.
This error means the browser waits to download and process these files before it can paint the first pixel of the page. Not just download them, but fully process them. A 20 KB CSS file that only styles a slider at the bottom of the page can delay the rendering of the entire page by 300 milliseconds. On a connection with 100ms latency, that single file adds three extra round trips to the page load.
The problem isn't just speed. Since 2021, Google has applied Core Web Vitals metrics in its search results, and LCP is one of the three main metrics. Render-blocking directly impacts LCP. So, this error isn't just an SEO suggestion; it's a ranking factor.
The solution isn't to delete files. The solution is understanding which resources are truly necessary for the initial render and which ones can wait. In this article, we'll examine all three tools in technical detail: critical CSS, the defer and async attributes, and preload. And most importantly, we'll explain where each one works and where it causes trouble.
Critical CSS: Only What's Needed for the Initial Render
Critical CSS means separating the styles related to the above-the-fold content and inlining them in the HTML. Keep the rest of the styles in a separate file and load them asynchronously.
A real-world example. Let's say your style.css file is 45 KB. The browser needs to download and parse this entire file before it can render the page. But of those 45 KB, perhaps only 8 KB relates to the header, main menu, and the first section of the page. Put those 8 KB in a <style> tag inside the <head>. The browser no longer waits for any external file.
Various tools exist for automatically generating critical CSS. The free 'critical' tool from npm is one of the most well-known:
npm install -g critical
critical --base https://example.com --width 1366 --height 768 --inline
This command renders the page at the specified dimensions and extracts the CSS relevant to that area. Place the output in the <head>, and load the rest of the CSS using the following method:
<link rel="stylesheet" href="/en/css/style.css" media="print" onload="this.media='all'">
This old but effective trick tells the browser to download the file with low priority (print) and then switch it back to normal after it loads. Modern browsers recognize this pattern and download the file without blocking rendering.
Where People Go Wrong
The most common mistake I've seen is this: a developer inlines all the CSS. They remove the external file and dump all 45 KB into a <style> tag. The result? The page's HTML goes from 30 KB to 75 KB. Now the browser has to download more HTML, and the time to first byte (TTFB) increases. And because the CSS is inside the HTML, it can't be cached either. Every visit downloads that same 75 KB again.
The sign of this mistake: your HTML size has unusually increased, and LCP hasn't improved or has even gotten worse. Critical CSS means only the above-the-fold styles, not all styles.
Another point: if your site is a single-page application (SPA) or uses a framework like React, manually generating critical CSS is nearly impossible. In that case, prefer automated tools within the framework (like Next.js, which inlines CSS by default).
Defer and Async: Two Attributes, Two Different Behaviors
For JavaScript, you have two attributes, each with different behavior. Their difference is subtle, but the result on load speed is quite noticeable.
The defer attribute tells the browser: download this file in parallel with the HTML, but delay its execution until the HTML parsing is complete. Deferred scripts execute in the order they appear in the HTML. This attribute is suitable for scripts that depend on the DOM.
The async attribute means: download the file in parallel and execute it as soon as it's ready. The execution order isn't guaranteed. The script that downloads first executes first. This attribute is suitable for independent scripts like analytics or ad injection scripts.
Which one should you choose? Simple rule: if your script needs the DOM and execution order matters, use defer. If the script is independent and has no dependencies, use async. If you use neither, your script becomes render-blocking, and the browser stops rendering until it fully downloads and executes.
An example of correct usage. Suppose you have two scripts: jquery.js and main.js, which depends on jQuery. Load both with defer. Execution order is preserved, and rendering isn't blocked. Now suppose you have an online chat script that's independent. Load it with async.
<script src="/js/jquery.js" defer></script>
<script src="/js/main.js" defer></script>
<script src="/js/live-chat.js" async></script>
When Defer Doesn't Work
Scripts that inject elements during document parsing (like some older advertising scripts) break with defer. Because defer postpones execution until after parsing, and those scripts expect to run mid-parse. If you notice that specific content doesn't appear on the page or the order of elements is messed up after adding defer, you're likely dealing with one of these scripts. In that case, place the script at the end of the <body> and remove the defer attribute. Yes, this isn't ideal, but sometimes it's the only practical solution for third-party scripts.
Preload: A Tool That Becomes the Bottleneck Itself
The rel="preload" attribute tells the browser: download this resource right now with high priority, because you'll need it soon. Its main use case is for fonts and hero images, which the browser doesn't know about until it encounters the relevant CSS.
<link rel="preload" href="/en/fonts/vazir.woff2" as="font" type="font/woff2" crossorigin>
This is exactly where preload comes in handy. Consider the Vazir font. The browser won't start downloading the font until it reads the CSS and realizes the font is needed. With preload, the font download starts from the very beginning and proceeds in parallel with the CSS.
But here's where people go wrong: they use preload for everything. Every image, every CSS file, every script. The result? The browser is downloading everything with high priority, and the browser's limited bandwidth is divided among these resources. Truly critical resources (like the main CSS) download slower. LCP gets worse. And you get a new warning in PageSpeed Insights: "Preload key requests."
My rule is this: use preload only for fonts and at most one or two critical resources that the browser discovers late. If you have more than 3-4 preloads, you're damaging the browser's prioritization system.
Another technical note: for fonts, make sure to include the crossorigin attribute. Without it, the font won't download, and you'll see a CORS error in the browser console. I've seen this error many times: a developer correctly added preload but forgot crossorigin, and the font still loads late.
The Right Order: Where to Start
If your site is WordPress, plugins like WP Rocket or Perfmatters automate these tasks. But if you have a custom site or want to do this manually, here's my suggested order:
- First, get a report with PageSpeed Insights or our speed test and SEO tools and see exactly which files are render-blocking.
- Review your CSS files. If your CSS file is large (over 20 KB) and only part of it is needed for the initial render, implement critical CSS.
- Categorize your JavaScript scripts: which ones need the DOM (defer), which are independent (async), and which need to run mid-parse (none — leave them at the end of the body).
- Load fonts with preload and don't forget the crossorigin attribute.
- Test again and compare the numbers.
An important note: after each change, make sure to test that the site is visually intact. Automated tools sometimes identify the wrong CSS as critical, and the result is a page whose styles apply gradually and with delay. The user sees the page without CSS and then suddenly everything falls into place. That experience is worse than slow loading.
If you're just getting started with Core Web Vitals metrics, I suggest you first read our Practical Guide to Core Web Vitals to get a fuller picture of the metrics. And if your problem goes beyond render-blocking and your overall site speed has dropped, the Complete Guide to Website Speed Optimization shows a step-by-step path.
Eliminating render-blocking isn't a one-time task. Every time you install a new plugin or add a new script, a new blocking resource might enter your page. A monthly check with PageSpeed Insights or a similar tool prevents the problem from gradually creeping back.
If you've done all this and your LCP is still high, the problem lies elsewhere. Maybe your images are heavy, or your server has slow response times. In that case, check out the Guide to Improving LCP and move on to image optimization with WebP and AVIF formats. Render-blocking is just one part of the puzzle; but it's the part where you can usually score points first and most easily.
And if you're doing this for a client's site and don't have enough time for manual implementation, our SEO services can manage this part of technical optimization for you. But if you're working manually, start right now: pick one CSS file and see what happens if you load it with media="print".
Frequently Asked Questions
What's the difference between defer and async, and which should I use?
defer executes the script after the HTML is fully parsed and preserves execution order. async executes the script as soon as it's ready with no guaranteed order. Use defer for DOM-dependent scripts and async for independent scripts like analytics.
Does eliminating render-blocking affect SEO?
Yes. Render-blocking directly impacts LCP, which is one of the Core Web Vitals metrics. Google considers these metrics in mobile ranking. Improving LCP can improve both user experience and your rankings.
Can I inline all my CSS?
No. Inlining all CSS significantly increases HTML size and eliminates the possibility of caching. Only inline the above-the-fold styles (critical CSS) and load the rest asynchronously.
Why isn't my font preload working?
The most likely reason is the absence of the crossorigin attribute in the preload tag. Fonts are loaded via CORS, and without crossorigin, the browser rejects the request. Make sure to add crossorigin to the tag.
Comments 0
No comments yet — be the first!