Render blocking resources: find them, and stop waiting on the wrong ones
Render blocking resources are the stylesheets and scripts a browser must download before it can paint the first pixel. This chapter lists them, separates the ones that actually cost you from the ones that only look expensive, and gives the two fixes that remove the wait — plus a one-line command that produces the list for any URL.

Render blocking resources are the stylesheets and scripts a browser must download and process before it can paint the first pixel. The reason is not a bug: the browser is protecting you from showing a page without its styling. The mistake is treating all of them as equally worth fixing. This chapter lists them, ranks them by what they actually cost, and removes the wait from the two that matter.
Read this first
You need one live page and a terminal. Nothing to install beyond the command below. If you have not read it yet, largest contentful paint is the metric this work is aimed at; render blocking resources are one of its most common causes. For what a panel of real sites does with its stylesheets, render blocking css on 26 homepages is the measured companion, and the script half of the same problem sits in unused JavaScript across the same panel.
Why a resource blocks the first paint
A browser cannot paint a complete page until it has both the DOM and the CSS object model. The DOM it builds as it parses your HTML. The CSS object model it can only build after every stylesheet it considers render-blocking has arrived, because a rule can change how anything already parsed looks. So a stylesheet in the head stops the first paint until it downloads and parses. Nothing is drawn early and corrected later; the browser simply waits.
A plain <script> blocks differently. It stops the HTML parser itself, because the script might write into the document. That delays the DOM, and it delays the paint behind it. A script with defer or async, or a module, downloads without holding the parser. Two mechanisms, one visible symptom: a blank screen while something on the network is still talking.
A render blocking resource is not slow. It is early. You fix it by changing when it is needed, not by making it smaller.
That distinction is what makes this topic worth a chapter. Compressing a blocking stylesheet is almost never the fix, because the wait is about ordering, not size. The fix is to decide which resources genuinely have to arrive before the first paint and to move the rest out of the way.
Do it in this order
Five steps, and each one ends in something you can check. Do not skip step two: it is what stops you optimising a file that was never the problem.
- List the blocking requests. Run the command below against the live URL. Done when you have a list of stylesheets and head scripts with a byte size next to each.
- Separate what must block from what must not. Mark each resource as needed for the first screen or not. Done when the "not" column is non-empty; on most sites it is the majority.
- Fix the stylesheet side. Inline the rules the first screen needs, and load the rest without holding rendering. Done when the head no longer contains a render-blocking link to the deferred sheet.
- Fix the script side. Move blocking head scripts to the end of the body, or add
defer. Done when no plain script sits between the opening<head>and the first paint. - Re-run and compare. The same command, the same page. Done when the request list is shorter and the largest contentful paint has moved earlier.
Step three is the one people get wrong most often, because the obvious move is to inline everything. That trade is real and it has a cost, which the failure section below spells out.
How to find your render blocking resources
The quickest list comes from Lighthouse's render blocking requests audit, which prints each blocking URL, its transfer size and the time it held up the render. It is one command, and it reads the same JSON the browser tooling prints.
npx lighthouse https://example.com/ \
--only-audits=render-blocking-insight \
--output=json --output-path=blocking.json --quiet
Run against github.com on 2 October 2026, the audit returns 20 render blocking requests. Every one of them is a stylesheet and not one is a script, which is the first useful thing the list tells you: the JavaScript on that page is already out of the way. The 20 sheets carry 279 KB between them. The largest is a 72 KB theme stylesheet, and the audit attributes 313 ms of blocking to it.
The one that cost the most was not the largest. A 7.5 KB sheet, roughly a tenth the size, is credited with 1,765 ms — more than five times the 72 KB file.
| Resource | Size | Blocking |
|---|---|---|
primer-react-brand-css.module.css | 72,143 B | 313 ms |
primer-4136ede8.css | 43,599 B | 313 ms |
global-54ba76e9.css | 40,846 B | 157 ms |
primer-react-css.module.css | 35,230 B | 157 ms |
light-99f877e9.css | 7,574 B | 1,765 ms |
Read one page, fetched once, as a demonstration of the command, not as a score for the whole site. The size column is the part you can act on, because it is the same every time. The duration column moves with the network, and on this run it says a small file can hold the render longer than a large one when it is discovered late or served slowly — a single reading is not a measurement of your site.
The deliverable: a table for deciding, not a number
The number of blocking resources is not itself useful. What you act on is the pairing of "does this have to be here before the first paint" with "how expensive is it to leave it here". Every blocking resource you meet falls into one of four rows.
| Resource | Why it blocks | What to do |
|---|---|---|
| Stylesheet for the first screen | CSSOM is needed before paint | Keep it, but keep it small; this is the one that earns its place |
| Stylesheet for below the fold | Same rule, but the rules are not needed yet | Defer it, or split the critical rules out and inline those |
Plain <script> in the head | Stops the parser, so the DOM stops too | Add defer; if it truly must run first, keep it tiny |
| Third-party tag loaded early | Sounds small, often is not, and you rarely control it | Load it after first paint, or not at all |
The single most useful habit is to sort the list by transfer size and then read the top of it out loud. A blocking resource that is both large and not needed for the first screen is the only kind worth a morning. The rest can wait for a slower day.
What goes wrong
Three failures show up again and again. All three look like progress in a browser on a fast machine.
- Inlining the entire stylesheet. Inlining removes the blocking request, so the first paint can be immediate. It also puts every byte of CSS into the HTML on every single navigation, where it cannot be cached on its own. On a site with a large sheet, you have traded a cacheable request for uncacheable weight on every page.
- Adding
deferto a script that had to run first. A deferred script runs after parsing, so anything that depended on it running early now runs late, or races the rest of the page. Defer is the right move only for scripts the first paint does not depend on. - Trusting the lab list as the site's verdict. A blocking resource on a fast CDN with a warm cache costs almost nothing, and the audit cannot tell that case apart from the expensive one. Use the list to decide what to move, then measure the field number to see whether moving it changed anything.
Common questions
How do I eliminate render blocking resources?
You do not eliminate all of them. You reduce the list to the resources the first screen genuinely needs, inline or preload those, and move the rest out of the critical path with media switching, defer, or by loading them after first paint. A page with zero blocking resources usually means everything was inlined, which has its own cost.
Does preload fix a render blocking resource?
No. Preload changes how early the browser starts fetching a file; it does not change whether the resource blocks rendering. A preloaded render-blocking stylesheet still blocks. Preload helps most when the resource is discovered late, such as a font named deep inside a stylesheet.
Is render blocking resources a ranking factor?
Not on its own. It matters because it delays the largest contentful paint, and Core Web Vitals feed the page experience signals. Google has never published a conversion from milliseconds of blocking to positions, so treat this as a user problem that is measured, not as a lever with a known price.
Does async remove render blocking?
For scripts, yes: async and defer both let the parser continue, so the script no longer blocks rendering. Neither attribute exists for stylesheets, which is why CSS needs a different fix. A stylesheet loaded with media="print" plus an onload that restores all is the pattern for that, and in our own survey of 26 homepages, none of them used it.
How many render blocking resources should a page have?
There is no target number, and anyone who gives you one is guessing. The useful question is whether each resource on the list is needed before the first paint. A short list of needed resources beats a long list of cheap ones, because the cost is the wait, not the count.
The boundary worth saying plainly: this chapter is about the browser's first paint. A crawler that fetches your HTML without rendering never waits for any of it, so a stylesheet that blocks a browser costs that client nothing. Whether a resource blocks is also a property of where it is discovered, so a sheet inserted by script needs blocking="render" to block at all — we have not tested that path, and we are not going to state a result we did not see. Keeping the blocking list short, and then watching the field number across the pages people land on, is part of what QueryWin works on.
Part of the QueryWin handbook · Level 2


